Skip to content

Commit f386125

Browse files
committed
test(search): expect no vector candidates for a dormant search index
A Search knowledge base's chunk writes now delete its candidate rows, so an explicit vector search of one finds nothing; hybrid search still decides access on the document
1 parent ad279c0 commit f386125

1 file changed

Lines changed: 36 additions & 30 deletions

File tree

‎apps/sim/lib/knowledge/__integration__/workspace-kb-document-access.integration.ts‎

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* nothing about what a workspace search returns. A signed-in reader's own grants widen what they
77
* read, and a source whose reader must be proven live admits a candidate only once the proof
88
* holds, with the readable rows below it filling the page when it does not. A Search-marked knowledge base
9-
* named explicitly by id is searched the same way.
9+
* named explicitly by id is searched the same way, except that it keeps no vector candidates.
1010
*/
1111
import { createHash } from 'node:crypto'
1212
import type { Principal } from '@sim/auth/principal'
@@ -48,10 +48,14 @@ afterAll(async () => {
4848
await db.$client.end()
4949
})
5050

51+
const ALL_MODES: readonly KnowledgeSearchMode[] = ['vector', 'hybrid']
52+
/** A Search knowledge base's chunk writes delete its candidate rows, so only keyword ranking finds it. */
53+
const KEYWORD_RANKED_MODES: readonly KnowledgeSearchMode[] = ['hybrid']
54+
5155
describe.each([
52-
['a workspace knowledge base', false],
53-
['a dormant search index', true],
54-
] as const)('search of %s decides access on the document', (_label, isSearchIndex) => {
56+
['a workspace knowledge base', false, ALL_MODES],
57+
['a dormant search index', true, KEYWORD_RANKED_MODES],
58+
] as const)('search of %s decides access on the document', (_label, isSearchIndex, modes) => {
5559
const ids = createKnowledgeAclFixtureIds()
5660
const baseId = generateId()
5761
const readable = [generateId(), generateId(), generateId()]
@@ -161,41 +165,43 @@ describe.each([
161165
return result.rows.map((row) => row.documentId).sort()
162166
}
163167

164-
it.each(['vector', 'hybrid'] as const)(
165-
'returns a signed-in reader the rows an actorless run gets (%s)',
166-
async (mode) => {
167-
const previousDebug = db.$client.options.debug
168-
const statements: string[] = []
169-
db.$client.options.debug = (_connection, query) => {
170-
statements.push(query)
171-
}
172-
try {
173-
const signedIn = await search(reader, mode)
174-
const workspaceKey = await search(actorless, mode)
175-
expect(signedIn).toEqual([...readable].sort())
176-
expect(workspaceKey).toEqual(signedIn)
177-
/** Nothing read the per-row machinery the search-index path keeps for itself. */
178-
for (const fragment of [
179-
'embedding_keyword_search',
180-
'knowledge_projection_dirty',
181-
'"embedding_search"."acl"',
182-
'"embedding_search"."connector_id"',
183-
]) {
184-
expect(statements.filter((statement) => statement.includes(fragment))).toEqual([])
185-
}
186-
} finally {
187-
db.$client.options.debug = previousDebug
168+
it.each(modes)('returns a signed-in reader the rows an actorless run gets (%s)', async (mode) => {
169+
const previousDebug = db.$client.options.debug
170+
const statements: string[] = []
171+
db.$client.options.debug = (_connection, query) => {
172+
statements.push(query)
173+
}
174+
try {
175+
const signedIn = await search(reader, mode)
176+
const workspaceKey = await search(actorless, mode)
177+
expect(signedIn).toEqual([...readable].sort())
178+
expect(workspaceKey).toEqual(signedIn)
179+
/** Nothing read the per-row machinery the search-index path keeps for itself. */
180+
for (const fragment of [
181+
'embedding_keyword_search',
182+
'knowledge_projection_dirty',
183+
'"embedding_search"."acl"',
184+
'"embedding_search"."connector_id"',
185+
]) {
186+
expect(statements.filter((statement) => statement.includes(fragment))).toEqual([])
188187
}
188+
} finally {
189+
db.$client.options.debug = previousDebug
189190
}
190-
)
191+
})
191192

192-
it.each(['vector', 'hybrid'] as const)(
193+
it.each(modes)(
193194
'returns a signed-in reader the document shared with them alone as well (%s)',
194195
async (mode) => {
195196
const owner: Principal = { kind: 'session', userId: ids.aliceId, sessionId: 'fixture-owner' }
196197
expect(await search(owner, mode)).toEqual([...readable, personal].sort())
197198
}
198199
)
200+
201+
it.runIf(isSearchIndex)('finds nothing by vector, since no candidate row is kept', async () => {
202+
const owner: Principal = { kind: 'session', userId: ids.aliceId, sessionId: 'fixture-owner' }
203+
expect(await search(owner, 'vector')).toEqual([])
204+
})
199205
})
200206

201207
describe('a workspace source whose reader must be proven live', () => {

0 commit comments

Comments
 (0)