Skip to content

test(ask): cover stopwords, multi-keyword, ties, order, and archived#11

Merged
jstuart0 merged 1 commit intojstuart0:mainfrom
mvanhorn:osc/10-ask-resolver-tests
Apr 24, 2026
Merged

test(ask): cover stopwords, multi-keyword, ties, order, and archived#11
jstuart0 merged 1 commit intojstuart0:mainfrom
mvanhorn:osc/10-ask-resolver-tests

Conversation

@mvanhorn
Copy link
Copy Markdown
Contributor

Summary

Adds the five test cases requested in #10 (stopword filtering, multi-keyword ranking, tie-break ordering, explicit-id order preservation, archived-session exclusion) plus one small implementation fix that the fetchSessionsById preserves input order test surfaced.

Closes #10.

Tests

All five tests use the shared src/server/services/ai/__test_db.ts helper, not a private sqlite instance (acceptance criterion 3).

  1. Stopword filtering - "is the api session healthy?" ends up scoring against api and healthy; is, the, and session are in STOPWORDS at resolver.ts:36-72. A session named api-gateway matches via api; a session named unrelated-name matches nothing.

  2. Multi-keyword match - "the sourcebridge oauth session" ranks a cwd /repos/sourcebridge/oauth-provider above /repos/sourcebridge/ui because scoreSession adds +2 per token-in-haystack hit (both tokens in the first, only one in the second).

  3. Stable ordering on ties - two sessions that score identically on target sort by lastActivityAt descending. Uses explicit timestamps (2020 vs 2030) to avoid racing on the default datetime('now') tick.

  4. fetchSessionsById preserves input order - passing ["b", "a"] returns ["b", "a"], not the SQLite IN-clause default. See implementation fix below.

  5. Archived sessions excluded - a session with status: "archived" (and isWorking: false) does not leak into the resolver pool. Inserts via db.insert(...) directly so the test exercises the resolver's WHERE clause without being restricted by the insertSession helper's narrower type union.

Test count before: 4. After: 9. bun test src/server/services/ask/resolver.test.ts runs all 9 green.

Implementation fix

fetchSessionsById at src/server/services/ask/resolver.ts:170 previously mapped the raw SQLite result:

const rows = await db.select().from(sessions).where(inArray(sessions.sessionId, ids));
return rows.map((row) => ({ ... }));

SQLite's IN clause doesn't preserve input order, so callers got back results in insertion / rowid order. The fix indexes the rows by sessionId and walks ids in the order the caller passed them:

const byId = new Map(rows.map((row) => [row.sessionId, row]));
return ids
  .map((id) => byId.get(id))
  .filter((row): row is (typeof rows)[number] => Boolean(row))
  .map((row) => ({ ... }));

This keeps "@mention"-style references in the UI stable regardless of how the sessions were inserted.

Testing

bun test src/server/services/ask/resolver.test.ts
#  9 pass
#  0 fail
#  15 expect() calls

bunx biome check src/server/services/ask/resolver.ts src/server/services/ask/resolver.test.ts
# Checked 2 files in 2ms. No fixes applied.

bunx tsc --noEmit
# (exit 0)

AI disclosure

This contribution was developed with AI assistance (Claude Code).

Adds the five test cases requested in jstuart0#10 and fixes one small
implementation bug that the order-preservation test surfaced.

Tests (all five against the shared __test_db.ts helper):

1. Stopword filtering - "is the api session healthy?" scores against
   "api" (and "healthy"); "is", "the", "session" are dropped via
   STOPWORDS and never reach scoreSession.
2. Multi-keyword match - a session whose cwd contains both
   "sourcebridge" and "oauth" outranks one that only matches one.
3. Stable ordering on ties - two identically-scored sessions order by
   lastActivityAt desc. Sets both timestamps explicitly to avoid
   racing on the datetime('now') tick.
4. fetchSessionsById preserves order - passing ["b", "a"] returns in
   that order, not the SQLite IN-clause's insertion/rowid order.
5. Archived sessions excluded - a session with status "archived" (and
   isWorking=false) does not leak into the resolver pool.

Implementation fix in resolver.ts fetchSessionsById: SQLite's IN
clause doesn't preserve input order. Re-index the returned rows by
sessionId and walk the caller's ids list to produce the output in the
requested order. This makes "@mention"-style references in the UI
stable regardless of insert order.

Closes jstuart0#10
@jstuart0 jstuart0 merged commit 21d8cef into jstuart0:main Apr 24, 2026
jstuart0 added a commit that referenced this pull request Apr 24, 2026
PR #11 changed fetchSessionsById to return rows in the caller's
input order instead of SQLite's rowid/insertion order. Internal
usage doesn't depend on ordering, but document it anyway so any
external consumer (e.g. a future @mention UI flow) can rely on it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unit-test coverage for the Ask session resolver

2 participants