@@ -21,7 +21,9 @@ vi.mock('@/connectors/registry.server', () => ({ CONNECTOR_REGISTRY: {} }))
2121const { drizzle } = await import ( 'drizzle-orm/postgres-js' )
2222const schema = await import ( '@sim/db/schema' )
2323const { persistDocumentAcls } = await import ( '@/lib/knowledge/connectors/sync-persistence' )
24- const { materializeDocumentAcls } = await import ( '@/lib/knowledge/connectors/member-observations' )
24+ const { materializeDocumentAcls, refreshObservedAclExpiry } = await import (
25+ '@/lib/knowledge/connectors/member-observations'
26+ )
2527const { mergeMirroredAcls, hideUnlistedDocuments } = await import (
2628 '@/lib/knowledge/connectors/mirrored-acls'
2729)
@@ -88,8 +90,7 @@ describe.runIf(Boolean(databaseUrl))('knowledge ACLs in PostgreSQL', () => {
8890 join = false ,
8991 githubInstallationGrants ?: GitHubInstallationReadGrant [ ] ,
9092 userId = 'reader' ,
91- confluenceSiteGrants ?: ConfluenceSiteReadGrant [ ] ,
92- storedAclFreshness = false
93+ confluenceSiteGrants ?: ConfluenceSiteReadGrant [ ]
9394 ) : Promise < boolean > {
9495 const query = new PgDialect ( ) . sqlToQuery (
9596 knowledgeAccessCondition ( {
@@ -98,7 +99,6 @@ describe.runIf(Boolean(databaseUrl))('knowledge ACLs in PostgreSQL', () => {
9899 tokens,
99100 githubInstallationGrants,
100101 confluenceSiteGrants,
101- storedAclFreshness,
102102 } )
103103 )
104104 const values = query . params . map ( ( value : unknown ) => {
@@ -504,10 +504,8 @@ describe.runIf(Boolean(databaseUrl))('knowledge ACLs in PostgreSQL', () => {
504504 * with the earliest observation it names, so one stale observer hides the document from readers
505505 * whose own observation is still current, until the sweep drops it and re-materializes.
506506 */
507- it ( 'decides mirrored freshness from the row, never admitting what per-candidate evidence refuses' , async ( ) => {
508- const proof = ( tokens : string [ ] , id : string ) => readable ( tokens , id )
509- const stored = ( tokens : string [ ] , id : string ) =>
510- readable ( tokens , id , false , undefined , 'reader' , undefined , true )
507+ it ( 'decides mirrored freshness from the row, falling back to evidence only where none is stored' , async ( ) => {
508+ const stored = ( tokens : string [ ] , id : string ) => readable ( tokens , id )
511509
512510 await putDocument ( 'admin-fresh' , [ 'u:alice@corp.com' ] )
513511 await connection . unsafe (
@@ -550,20 +548,32 @@ describe.runIf(Boolean(databaseUrl))('knowledge ACLs in PostgreSQL', () => {
550548 [ 'members-fresh' , [ alice ] , true ] ,
551549 [ 'members-stale' , [ alice ] , false ] ,
552550 ] as const ) {
553- expect ( await proof ( [ ...tokens ] , id ) ) . toBe ( expected )
554551 expect ( await stored ( [ ...tokens ] , id ) ) . toBe ( expected )
555552 }
556- /** Stricter, never wider: the fresh observer waits for the sweep. */
557- expect ( await proof ( [ alice ] , 'members-mixed' ) ) . toBe ( true )
553+ /** Stricter than the evidence, never wider: the fresh observer waits for the sweep. */
558554 expect ( await stored ( [ alice ] , 'members-mixed' ) ) . toBe ( false )
559- expect ( await proof ( [ bob ] , 'members-mixed' ) ) . toBe ( false )
560555 expect ( await stored ( [ bob ] , 'members-mixed' ) ) . toBe ( false )
561- /** A row the backfill has not reached yet is unreadable rather than assumed current. */
562- expect ( await proof ( [ 'u:alice@corp.com' ] , 'admin-unbackfilled' ) ) . toBe ( true )
556+ /**
557+ * Scope renewal extends an observation without changing who observes the document; carrying
558+ * that into the stored expiry is what makes the document readable again.
559+ */
560+ await connection . unsafe (
561+ `UPDATE knowledge_document_observation SET last_seen_at = statement_timestamp()
562+ WHERE document_id = 'members-mixed' AND member_id = 'bob'`
563+ )
564+ expect ( await stored ( [ alice ] , 'members-mixed' ) ) . toBe ( false )
565+ await refreshObservedAclExpiry ( 'members' , [ 'members-mixed' ] , drizzle ( connection , { schema } ) )
566+ expect ( await stored ( [ alice ] , 'members-mixed' ) ) . toBe ( true )
567+ expect ( await stored ( [ bob ] , 'members-mixed' ) ) . toBe ( true )
568+
569+ /** A row no current writer has touched still proves freshness from its evidence. */
570+ expect ( await stored ( [ 'u:alice@corp.com' ] , 'admin-unbackfilled' ) ) . toBe ( true )
571+ await connection . unsafe (
572+ "UPDATE document SET acl_verified_at = statement_timestamp() - interval '25 hours' WHERE id = 'admin-unbackfilled'"
573+ )
563574 expect ( await stored ( [ 'u:alice@corp.com' ] , 'admin-unbackfilled' ) ) . toBe ( false )
564575 /** Uploads and workspace-mode documents carry no expiry and are unaffected. */
565576 await connection . unsafe ( "INSERT INTO document(id) VALUES ('upload')" )
566- expect ( await proof ( [ 'ws' ] , 'upload' ) ) . toBe ( true )
567577 expect ( await stored ( [ 'ws' ] , 'upload' ) ) . toBe ( true )
568578 } )
569579
@@ -608,7 +618,19 @@ describe.runIf(Boolean(databaseUrl))('knowledge ACLs in PostgreSQL', () => {
608618 expect ( await readable ( [ page ] , 'persisted' ) ) . toBe ( false )
609619 expect ( await readable ( [ space , page ] , 'persisted' ) ) . toBe ( true )
610620 await connection . unsafe (
611- "UPDATE document SET acl_verified_at = statement_timestamp() - interval '25 hours'"
621+ `UPDATE document SET acl_verified_at = statement_timestamp() - interval '25 hours',
622+ acl_valid_until = statement_timestamp() - interval '1 hour'`
623+ )
624+ expect ( await readable ( [ space , page ] , 'persisted' ) ) . toBe ( false )
625+ await persistDocumentAcls ( 'admin' , input , executor )
626+ expect ( await readable ( [ space , page ] , 'persisted' ) ) . toBe ( true )
627+ /**
628+ * A rolling-deploy writer that knows only the evidence column leaves the stored expiry behind,
629+ * and the document waits for a current writer instead of being read from stale state.
630+ */
631+ await connection . unsafe (
632+ `UPDATE document SET acl_verified_at = statement_timestamp(),
633+ acl_valid_until = statement_timestamp() - interval '1 hour'`
612634 )
613635 expect ( await readable ( [ space , page ] , 'persisted' ) ) . toBe ( false )
614636 await persistDocumentAcls ( 'admin' , input , executor )
0 commit comments