Skip to content

Commit d43d20a

Browse files
committed
test(knowledge): assert the ACL write's fan-out, not unfilled rows the trigger now skips
The projection trigger no longer writes an ACL onto chunks the backfill has not filled, so the ACL write tests can no longer see an unchanged write through those rows. They now count the document writes that fire the fan-out trigger, and expect a changed ACL on filled chunks only.
1 parent 23c8bc7 commit d43d20a

1 file changed

Lines changed: 24 additions & 7 deletions

File tree

‎apps/sim/lib/knowledge/connectors/sync-persistence.postgres.test.ts‎

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ describe.runIf(Boolean(databaseUrl))('persistDocumentAcls in PostgreSQL', () =>
3333
let sql: Sql
3434
const schemaName = `acl_write_${generateId().replaceAll('-', '')}`
3535

36+
const fannedOut = async () =>
37+
(
38+
await sql<{ document_id: string }[]>`SELECT document_id FROM fan_out ORDER BY document_id`
39+
).map((row) => row.document_id)
40+
3641
const projected = () =>
3742
sql<{ id: string; acl: string[] | null }[]>`
3843
SELECT id, acl FROM embedding_search
@@ -68,6 +73,15 @@ describe.runIf(Boolean(databaseUrl))('persistDocumentAcls in PostgreSQL', () =>
6873
)`
6974
}
7075
await installProjectionSourceAcl(sql)
76+
/**
77+
* Counts the documents whose write fired the projection fan-out: the trigger fires on any
78+
* assignment of `acl`, changed or not, so this is the cost an unchanged write must not pay.
79+
*/
80+
await sql`CREATE TABLE fan_out (document_id text NOT NULL)`
81+
await sql.unsafe(`CREATE FUNCTION count_fan_out() RETURNS trigger LANGUAGE plpgsql AS $$
82+
BEGIN INSERT INTO fan_out VALUES (NEW.id); RETURN NEW; END; $$`)
83+
await sql`CREATE TRIGGER count_fan_out AFTER UPDATE OF connector_id, acl ON document
84+
FOR EACH ROW EXECUTE FUNCTION count_fan_out()`
7185
await sql`ALTER TABLE embedding_search DISABLE TRIGGER embedding_search_source_acl_set`
7286
await sql`ALTER TABLE embedding_keyword_tin DISABLE TRIGGER embedding_keyword_tin_source_acl_set`
7387
}, 60_000)
@@ -79,7 +93,7 @@ describe.runIf(Boolean(databaseUrl))('persistDocumentAcls in PostgreSQL', () =>
7993
})
8094

8195
beforeEach(async () => {
82-
await sql`TRUNCATE embedding_search, embedding_keyword_tin, document`
96+
await sql`TRUNCATE embedding_search, embedding_keyword_tin, document, fan_out`
8397
await sql`INSERT INTO document (id, external_id, connector_id, acl, acl_verified_at) VALUES
8498
('doc-same', 'file-same', 'admin', ARRAY[${ALICE}], now() - interval '1 day'),
8599
('doc-moved', 'file-moved', 'admin', ARRAY[${ALICE}], now() - interval '1 day')`
@@ -93,7 +107,7 @@ describe.runIf(Boolean(databaseUrl))('persistDocumentAcls in PostgreSQL', () =>
93107
}
94108
})
95109

96-
it('refreshes the evidence of an unchanged ACL without rewriting any chunk projection row', async () => {
110+
it('refreshes the evidence of an unchanged ACL without firing the projection fan-out', async () => {
97111
await expect(persist(new Map([['file-same', [ALICE]]]))).resolves.toEqual({
98112
updated: 1,
99113
rejected: 0,
@@ -103,6 +117,7 @@ describe.runIf(Boolean(databaseUrl))('persistDocumentAcls in PostgreSQL', () =>
103117
SELECT acl, acl_verified_at > now() AT TIME ZONE 'UTC' - interval '1 minute' AS fresh
104118
FROM document WHERE id = 'doc-same'`
105119
expect(stored).toEqual({ acl: [ALICE], fresh: true })
120+
expect(await fannedOut()).toEqual([])
106121
expect((await projected()).filter((row) => row.id.includes('-same-'))).toEqual([
107122
{ id: 'kw-same-filled', acl: [ALICE] },
108123
{ id: 'kw-same-unfilled', acl: null },
@@ -111,7 +126,8 @@ describe.runIf(Boolean(databaseUrl))('persistDocumentAcls in PostgreSQL', () =>
111126
])
112127
})
113128

114-
it('propagates a changed ACL to every chunk projection row, filled or not', async () => {
129+
/** A chunk the backfill has not filled keeps a NULL ACL; the backfill copies the current one. */
130+
it('propagates a changed ACL to every filled chunk projection row', async () => {
115131
await expect(persist(new Map([['file-moved', [BOB]]]))).resolves.toEqual({
116132
updated: 1,
117133
rejected: 0,
@@ -121,9 +137,9 @@ describe.runIf(Boolean(databaseUrl))('persistDocumentAcls in PostgreSQL', () =>
121137
expect(stored.acl).toEqual([BOB])
122138
expect((await projected()).filter((row) => row.id.includes('-moved-'))).toEqual([
123139
{ id: 'kw-moved-filled', acl: [BOB] },
124-
{ id: 'kw-moved-unfilled', acl: [BOB] },
140+
{ id: 'kw-moved-unfilled', acl: null },
125141
{ id: 'vec-moved-filled', acl: [BOB] },
126-
{ id: 'vec-moved-unfilled', acl: [BOB] },
142+
{ id: 'vec-moved-unfilled', acl: null },
127143
])
128144
})
129145

@@ -156,9 +172,10 @@ describe.runIf(Boolean(databaseUrl))('persistDocumentAcls in PostgreSQL', () =>
156172
{ id: 'doc-moved', acl: [ALICE, BOB] },
157173
{ id: 'doc-same', acl: [ALICE] },
158174
])
175+
expect(await fannedOut()).toEqual(['doc-moved'])
159176
expect(
160-
(await projected()).filter((row) => row.id.endsWith('-unfilled')).map((row) => row.acl)
161-
).toEqual([[ALICE, BOB], null, [ALICE, BOB], null])
177+
(await projected()).filter((row) => row.id.endsWith('-filled')).map((row) => row.acl)
178+
).toEqual([[ALICE, BOB], [ALICE], [ALICE, BOB], [ALICE]])
162179
})
163180
it('writes a changed ACL group larger than one change batch completely', async () => {
164181
const ids = Array.from(

0 commit comments

Comments
 (0)