const { test, expect, describe } = require('bun:test'); const publicRead = require('./public-read.service'); const { createKeyedSerializer } = require('./keyed-serializer'); const { GRANTED_AT, grantedQuery, recordGrantUpdate, forgetGrantUpdate } = require('./public-read-ledger'); const ALICE = 'http://localhost:3000/alice'; const BOB = 'http://localhost:3000/bob'; const E = `${ALICE}/data/events/e1`; const C = `${ALICE}/data/collections/e1-attendees`; const FOAF_AGENT = 'http://xmlns.com/foaf/0.1/Agent'; const APP = 'http://localhost:3003/as/actor/app'; // The ACL document shape `pod-permissions.get` returns (compacted with the acl:/foaf: prefixes). // Note: for an event, the owner's rights come from the container's inherited #Default rules — the // event's only OWN rule is #Read foaf:Agent (confirmed live 2026-10-12). const PUBLIC_READ_AUTH = { '@id': '#Read', 'acl:mode': 'acl:Read', 'acl:agentClass': 'foaf:Agent' }; const DEFAULT_READ = { '@id': '#DefaultRead', 'acl:mode': 'acl:Read', 'acl:agent': ALICE }; // A revoke PUT returns 204; capture its body so tests can assert the reconstructed graph. const okProxy = captured => params => { captured.put = params; return { status: 204 }; }; const publicEvent = (extra = {}) => ({ id: E, type: 'Event', to: 'as:Public', ...extra }); const privateEvent = (extra = {}) => ({ id: E, type: 'Event', name: 'now private', ...extra }); /** `pod-resources.get` answering from a map of uri → stored body (404 for anything else). */ const stored = (...bodies) => ({ resourceUri }) => { const body = bodies.find(b => b.id === resourceUri); return body ? { ok: true, status: 200, body } : { ok: false, status: 404 }; }; /** * The backend's grant ledger, answering exactly the queries `public-read-ledger.js` builds. * `granted` lists the resources Festipod is recorded as having made public. */ function ledgerCall(granted, action, params) { const match = /<([^>]+)> /.exec(params.query); if (!match) throw new Error(`unexpected store call ${params.query}`); const uri = match[1]; if (action === 'triplestore.query' && params.query === grantedQuery(uri)) return granted.has(uri); if (action === 'triplestore.update' && params.query === recordGrantUpdate(uri)) return void granted.add(uri); if (action === 'triplestore.update' && params.query === forgetGrantUpdate(uri)) return void granted.delete(uri); throw new Error(`unexpected store call ${params.query}`); } /** * The service with its actions+methods bound to a recording logger, over a programmable pod and a * grant ledger (by default: Festipod made both the event and its collection public). */ function service(pod, { granted = [E, C], timeoutMs } = {}) { const logs = { info: [], warn: [], error: [] }; const calls = []; const ledger = new Set(granted); const svc = { logger: { info: m => logs.info.push(m), warn: m => logs.warn.push(m), error: m => logs.error.push(m) }, actions: {} }; if (publicRead.created) publicRead.created.call(svc); if (timeoutMs) svc.serialize = createKeyedSerializer({ timeoutMs }); const ctx = { call: async (action, params) => { calls.push({ action, params }); if (action.startsWith('triplestore.')) return ledgerCall(ledger, action, params); if (action === 'pod-permissions.fetch' && !pod['pod-permissions.fetch']) { // The ACL read, served from a test's `pod-permissions.get` (authorizations, or `false` = a failed read). const uri = params.url.replace(/^(https?:\/\/[^/]+)\/_acl/, '$1'); const authorizations = pod['pod-permissions.get'] ? await pod['pod-permissions.get']({ uri }) : undefined; return authorizations === false || authorizations === undefined ? { status: 500 } : { status: 200, body: { '@graph': authorizations } }; } return pod[action] ? pod[action](params) : undefined; } }; // Bind actions so `this.actions.reconcile|grant|revoke(...)` work from the methods. for (const [name, def] of Object.entries(publicRead.actions)) { svc.actions[name] = (params, opts) => def.handler.call(svc, { ...opts, params, call: ctx.call }); } for (const [name, fn] of Object.entries(publicRead.methods)) svc[name] = fn.bind(svc); return { svc, ctx, logs, calls, ledger }; } // The agent triples a PUT revocation graph carries, flattened to {auth, p, o} for assertions. function putTriples(putParams) { const graph = JSON.parse(putParams.body)['@graph']; const out = []; for (const node of graph) { for (const p of ['acl:agent', 'acl:agentClass', 'acl:agentGroup', 'acl:origin']) { for (const o of [].concat(node[p] ?? [])) out.push({ auth: node['@id'], p, o }); } } return out; } describe('public-read.onUpdate — the grant and the ACL stay consistent with visibility', () => { test('private → public: grants acl:Read to foaf:Agent (and is idempotent)', async () => { const pod = { 'pod-resources.get': stored(publicEvent()), 'pod-permissions.get': () => [DEFAULT_READ], 'pod-permissions.add': () => true }; const { svc, ctx, calls } = service(pod); await svc.onUpdate(ctx, publicEvent(), ALICE); const add = calls.find(c => c.action === 'pod-permissions.add'); expect(add).toBeDefined(); expect(add.params).toMatchObject({ uri: E, agentPredicate: 'acl:agentClass', agentUri: FOAF_AGENT, mode: 'acl:Read' }); expect(calls.some(c => c.action === 'signature.proxy.query')).toBe(false); }); test('already public twin: grant finds the rule present and adds nothing', async () => { const pod = { 'pod-resources.get': stored(publicEvent()), 'pod-permissions.get': () => [DEFAULT_READ, PUBLIC_READ_AUTH], 'pod-permissions.add': () => { throw new Error('must not add'); } }; const { svc, ctx, calls } = service(pod); await svc.onUpdate(ctx, publicEvent(), ALICE); expect(calls.some(c => c.action === 'pod-permissions.add')).toBe(false); }); test('public → private: PUTs a graph that drops foaf:Agent and keeps the owner (set not empty)', async () => { const captured = {}; const pod = { 'pod-resources.get': stored(privateEvent()), 'pod-permissions.get': () => [DEFAULT_READ, PUBLIC_READ_AUTH], 'app.get': () => ({ id: APP }), 'signature.proxy.query': okProxy(captured) }; const { svc, ctx, calls } = service(pod); await svc.onUpdate(ctx, privateEvent(), ALICE); const put = calls.find(c => c.action === 'signature.proxy.query'); expect(put).toBeDefined(); expect(put.params).toMatchObject({ method: 'PUT', url: `http://localhost:3000/_acl/alice/data/events/e1`, actorUri: APP }); const triples = putTriples(captured.put); // No foaf:Agent anywhere in the PUT; the #Default rule is NOT re-emitted (a PUT leaves it be). expect(triples.some(t => t.p === 'acl:agentClass' && (t.o === 'foaf:Agent' || t.o === FOAF_AGENT))).toBe(false); expect(triples.some(t => t.auth.includes('#Default'))).toBe(false); // The own set is non-empty: the owner is kept as acl:agent of #Read. expect(triples.some(t => t.p === 'acl:agent' && t.o === ALICE && t.auth.includes('#Read'))).toBe(true); }); test('public → private when the ACL stored the FULL foaf IRI: still dropped', async () => { const captured = {}; const fullIriAuth = { '@id': '#Read', 'acl:mode': 'acl:Read', 'acl:agentClass': FOAF_AGENT }; const pod = { 'pod-resources.get': stored(privateEvent()), 'pod-permissions.get': () => [DEFAULT_READ, fullIriAuth], 'app.get': () => ({ id: APP }), 'signature.proxy.query': okProxy(captured) }; const { svc, ctx } = service(pod); await svc.onUpdate(ctx, privateEvent(), ALICE); expect(putTriples(captured.put).some(t => t.o === FOAF_AGENT || t.o === 'foaf:Agent')).toBe(false); }); test('a per-contact own grant is preserved when the public rule is removed', async () => { const captured = {}; const friend = 'http://localhost:3000/carol'; const readWithFriend = { '@id': '#Read', 'acl:mode': 'acl:Read', 'acl:agentClass': 'foaf:Agent', 'acl:agent': friend }; const pod = { 'pod-resources.get': stored(privateEvent()), 'pod-permissions.get': () => [DEFAULT_READ, readWithFriend], 'app.get': () => ({ id: APP }), 'signature.proxy.query': okProxy(captured) }; const { svc, ctx } = service(pod); await svc.onUpdate(ctx, privateEvent(), ALICE); const triples = putTriples(captured.put); expect(triples.some(t => t.p === 'acl:agent' && t.o === friend)).toBe(true); // friend kept expect(triples.some(t => t.o === 'foaf:Agent' || t.o === FOAF_AGENT)).toBe(false); // public dropped }); test('update to private on an already-private event: nothing to revoke, no PUT', async () => { const pod = { 'pod-resources.get': stored(privateEvent({ name: 'still private' })), 'pod-permissions.get': () => [DEFAULT_READ], 'signature.proxy.query': () => { throw new Error('must not PUT'); } }; const { svc, ctx, calls } = service(pod); await svc.onUpdate(ctx, privateEvent({ name: 'still private' }), ALICE); expect(calls.some(c => c.action === 'signature.proxy.query')).toBe(false); }); test('a pod refusal of the revoke PUT is logged, never thrown', async () => { const pod = { 'pod-resources.get': stored(privateEvent()), 'pod-permissions.get': () => [DEFAULT_READ, PUBLIC_READ_AUTH], 'app.get': () => ({ id: APP }), 'signature.proxy.query': () => ({ status: 400 }) }; const { svc, ctx, logs } = service(pod); await expect(svc.onUpdate(ctx, privateEvent(), ALICE)).resolves.toBeUndefined(); expect(logs.error.some(m => m.includes('HTTP 400'))).toBe(true); }); test('a pod failure is logged, never thrown (the watcher is not awaited)', async () => { const pod = { 'pod-resources.get': stored(publicEvent()), 'pod-permissions.get': () => { throw new Error('pod down'); } }; const { svc, ctx, logs } = service(pod); await expect(svc.onUpdate(ctx, publicEvent(), ALICE)).resolves.toBeUndefined(); expect(logs.error[0]).toContain('pod down'); }); test('a failing read of the event itself is logged, never thrown', async () => { const pod = { 'pod-resources.get': () => { throw new Error('proxy unreachable'); } }; const { svc, ctx, logs } = service(pod); await expect(svc.onUpdate(ctx, publicEvent(), ALICE)).resolves.toBeUndefined(); expect(logs.error[0]).toContain('proxy unreachable'); }); }); describe('public-read.onCreate — grants only on public intent', () => { test('a created public event is granted', async () => { const pod = { 'pod-resources.get': stored(publicEvent()), 'pod-permissions.get': () => [DEFAULT_READ], 'pod-permissions.add': () => true }; const { svc, ctx, calls } = service(pod); await svc.onCreate(ctx, publicEvent(), ALICE); expect(calls.some(c => c.action === 'pod-permissions.add')).toBe(true); }); test('a created private event is not granted', async () => { const pod = { 'pod-resources.get': stored(privateEvent({ name: 'private' })), 'pod-permissions.get': () => [DEFAULT_READ] }; const { svc, ctx, calls, logs } = service(pod); await svc.onCreate(ctx, privateEvent({ name: 'private' }), ALICE); expect(calls.some(c => c.action === 'pod-permissions.add')).toBe(false); expect(calls.some(c => c.action === 'signature.proxy.query')).toBe(false); expect(logs.info.some(m => m.includes('not addressed to the Public collection'))).toBe(true); }); }); describe('public-read.reconcile — decides on the STORED event, read when acting', () => { test('the notification\'s copy is only a pointer: a "public" copy of an event the pod holds private grants nothing', async () => { const pod = { 'pod-resources.get': stored(privateEvent()), 'pod-permissions.get': () => [DEFAULT_READ], 'pod-permissions.add': () => true }; const { svc, ctx, calls } = service(pod); await svc.onUpdate(ctx, publicEvent(), ALICE); expect(calls.some(c => c.action === 'pod-permissions.add')).toBe(false); }); test('a public Note stored at the URI is not an event: nothing granted (the type is re-checked)', async () => { const note = { id: E, type: 'Note', to: 'as:Public' }; const pod = { 'pod-resources.get': stored(note), 'pod-permissions.get': () => [DEFAULT_READ], 'pod-permissions.add': () => true }; const { svc, ctx, calls, logs } = service(pod); await svc.onCreate(ctx, E, ALICE); expect(calls.some(c => c.action.startsWith('pod-permissions'))).toBe(false); expect(logs.warn[0]).toContain('not an as:Event'); }); test("an event outside the actor's pod is neither read nor touched", async () => { const bobs = `${BOB}/data/events/e1`; const { svc, ctx, calls, logs } = service({}); await svc.onUpdate(ctx, bobs, ALICE); expect(calls).toEqual([]); expect(logs.warn[0]).toContain(`not in the pod of ${ALICE}`); }); test('a deleted event is left alone (its ACL went with it)', async () => { const { svc, ctx, calls } = service({ 'pod-resources.get': () => ({ status: 404 }) }); await svc.onUpdate(ctx, E, ALICE); expect(calls.map(c => c.action)).toEqual(['pod-resources.get']); }); test('CONCURRENT notifications: the newest state wins (no grant lands after the revoke)', async () => { // A stateful pod: the event's addressing, and whether its ACL holds the public-read rule. // The grant is slow (the pod's latency); the event is made private while the first handler acts. let current = publicEvent(); let aclIsPublic = false; const pod = { 'pod-resources.get': ({ resourceUri }) => (resourceUri === E ? { status: 200, body: current } : { status: 404 }), 'pod-permissions.get': () => (aclIsPublic ? [DEFAULT_READ, PUBLIC_READ_AUTH] : [DEFAULT_READ]), 'pod-permissions.add': async () => { await new Promise(resolve => setTimeout(resolve, 30)); aclIsPublic = true; return true; }, 'app.get': () => ({ id: APP }), 'signature.proxy.query': ({ body }) => { aclIsPublic = putTriples({ body }).some(t => t.o === 'foaf:Agent' || t.o === FOAF_AGENT); return { status: 204 }; } }; const { svc, ctx } = service(pod); const first = svc.onUpdate(ctx, publicEvent(), ALICE); // notified while public await new Promise(resolve => setTimeout(resolve, 5)); current = privateEvent(); // the owner makes it private; a second notification follows const second = svc.onUpdate(ctx, privateEvent(), ALICE); await Promise.all([first, second]); expect(aclIsPublic).toBe(false); }); }); describe("public-read.reconcile — the event's attendees collection follows its visibility, both ways", () => { const COLLECTION_DEFAULT = { '@id': '#Read', 'acl:mode': 'acl:Read', 'acl:agentClass': 'foaf:Agent' }; test('public: the event AND its collection are granted (the collection already is: nothing added)', async () => { const granted = []; const pod = { 'pod-resources.get': stored(publicEvent({ 'apods:attendees': C })), 'pod-permissions.get': ({ uri }) => (uri === C ? [COLLECTION_DEFAULT] : [DEFAULT_READ]), 'pod-permissions.add': ({ uri }) => granted.push(uri) }; const { svc, ctx, calls } = service(pod); await svc.onUpdate(ctx, E, ALICE); expect(calls.filter(c => c.action === 'pod-permissions.fetch').map(c => c.params.url)).toEqual([ 'http://localhost:3000/_acl/alice/data/events/e1', 'http://localhost:3000/_acl/alice/data/collections/e1-attendees' ]); expect(granted).toEqual([E]); }); test('private: the public read of the event AND of its collection are revoked', async () => { const puts = []; const pod = { 'pod-resources.get': stored(privateEvent({ 'apods:attendees': C })), 'pod-permissions.get': ({ uri }) => (uri === C ? [COLLECTION_DEFAULT] : [DEFAULT_READ, PUBLIC_READ_AUTH]), 'app.get': () => ({ id: APP }), 'signature.proxy.query': params => { puts.push(params.url); return { status: 204 }; } }; const { svc, ctx } = service(pod); await svc.onUpdate(ctx, E, ALICE); expect(puts).toEqual(['http://localhost:3000/_acl/alice/data/events/e1', 'http://localhost:3000/_acl/alice/data/collections/e1-attendees']); }); test('private → public: the collection is granted back', async () => { const granted = []; const pod = { 'pod-resources.get': stored(publicEvent({ 'http://activitypods.org/ns/core#attendees': { id: C } })), 'pod-permissions.get': () => [DEFAULT_READ], 'pod-permissions.add': ({ uri }) => granted.push(uri) }; const { svc, ctx } = service(pod); await svc.onUpdate(ctx, E, ALICE); expect(granted).toEqual([E, C]); }); test('a collection named outside the event\'s pod is left alone', async () => { const foreign = `${BOB}/data/collections/x`; const pod = { 'pod-resources.get': stored(privateEvent({ 'apods:attendees': foreign })), 'pod-permissions.get': () => [DEFAULT_READ] }; const { svc, ctx, calls, logs } = service(pod); await svc.onUpdate(ctx, E, ALICE); expect(calls.some(c => c.params && c.params.uri === foreign)).toBe(false); expect(logs.warn.some(m => m.includes('outside its pod'))).toBe(true); }); test('a public read INHERITED from the container cannot be revoked: reported, no PUT', async () => { const inherited = { '@id': '#DefaultRead', 'acl:mode': 'acl:Read', 'acl:agentClass': 'foaf:Agent' }; const pod = { 'pod-resources.get': stored(privateEvent({ 'apods:attendees': C })), 'pod-permissions.get': ({ uri }) => (uri === C ? [inherited] : [DEFAULT_READ]), 'signature.proxy.query': () => { throw new Error('must not PUT'); } }; const { svc, ctx, logs } = service(pod); await svc.onUpdate(ctx, E, ALICE); expect(logs.warn.some(m => m.includes(`${C} is publicly readable through a rule inherited`))).toBe(true); expect(logs.error).toEqual([]); }); }); describe('public-read — revokes ONLY a public read Festipod granted (the persisted grant ledger)', () => { test('a public read Festipod did not add (owner, another app) is left untouched and logged', async () => { const pod = { 'pod-resources.get': stored(privateEvent()), 'pod-permissions.get': () => [DEFAULT_READ, PUBLIC_READ_AUTH], 'signature.proxy.query': () => { throw new Error('must not PUT'); } }; const { svc, ctx, calls, logs } = service(pod, { granted: [] }); await svc.onUpdate(ctx, privateEvent(), ALICE); expect(calls.some(c => c.action === 'signature.proxy.query')).toBe(false); expect(logs.info.some(m => m.includes('not through a grant Festipod made: left untouched'))).toBe(true); expect(logs.error).toEqual([]); }); test('a grant is recorded BEFORE it is made, and a revocation forgets it', async () => { let aclIsPublic = false; let recordedWhenAdded; const pod = { 'pod-resources.get': stored(publicEvent()), 'pod-permissions.get': () => (aclIsPublic ? [DEFAULT_READ, PUBLIC_READ_AUTH] : [DEFAULT_READ]), 'app.get': () => ({ id: APP }), 'signature.proxy.query': () => { aclIsPublic = false; return { status: 204 }; } }; const w = service(pod, { granted: [] }); pod['pod-permissions.add'] = () => { recordedWhenAdded = w.ledger.has(E); aclIsPublic = true; return true; }; await w.svc.onUpdate(w.ctx, E, ALICE); expect(recordedWhenAdded).toBe(true); expect(w.ledger.has(E)).toBe(true); pod['pod-resources.get'] = stored(privateEvent()); await w.svc.onUpdate(w.ctx, E, ALICE); expect(aclIsPublic).toBe(false); expect(w.ledger.has(E)).toBe(false); }); test('already world-readable before Festipod: nothing granted, nothing recorded', async () => { const pod = { 'pod-resources.get': stored(publicEvent()), 'pod-permissions.get': () => [DEFAULT_READ, PUBLIC_READ_AUTH] }; const w = service(pod, { granted: [] }); await w.svc.onUpdate(w.ctx, E, ALICE); expect(w.ledger.has(E)).toBe(false); }); test('a grant the pod refuses is not left in the record', async () => { const pod = { 'pod-resources.get': stored(publicEvent()), 'pod-permissions.get': () => [DEFAULT_READ], 'pod-permissions.add': () => false }; const w = service(pod, { granted: [] }); await w.svc.onUpdate(w.ctx, E, ALICE); expect(w.ledger.has(E)).toBe(false); expect(w.logs.error.some(m => m.includes('refused the public-read grant'))).toBe(true); }); test('recordCreated records a collection Festipod created, without depending on an ACL read', async () => { const w = service({}, { granted: [] }); // no ACL reachable at all expect(await w.svc.actions.recordCreated({ resourceUri: C, actorUri: ALICE })).toBe(true); expect([...w.ledger]).toEqual([C]); }); test('the ledger predicate is Festipod\'s own', () => { expect(GRANTED_AT).toBe('http://festipod.org/publicReadGrantedAt'); expect(grantedQuery(E)).toBe(`ASK { <${E}> <${GRANTED_AT}> ?at }`); }); }); describe('public-read — an unreadable ACL is UNKNOWN, never "not public" (second review)', () => { const OWNER_SHARE = [DEFAULT_READ, PUBLIC_READ_AUTH]; test('revoke on a transient ACL failure keeps Festipod\'s record; the next notification revokes', async () => { let gets = 0; let puts = 0; const pod = { 'pod-resources.get': stored(privateEvent()), 'pod-permissions.get': () => (gets++ === 0 ? false : OWNER_SHARE), // one non-200, then the real ACL 'app.get': () => ({ id: APP }), 'signature.proxy.query': () => { puts++; return { status: 204 }; } }; const w = service(pod, { granted: [E] }); await w.svc.onUpdate(w.ctx, E, ALICE); expect(puts).toBe(0); expect(w.ledger.has(E)).toBe(true); // kept: the state was unknown expect(w.logs.warn.some(m => m.includes('could not be read'))).toBe(true); await w.svc.onUpdate(w.ctx, E, ALICE); expect(puts).toBe(1); expect(w.ledger.has(E)).toBe(false); }); test("grant on a transient ACL failure records nothing, so the owner's own share is never taken for Festipod's", async () => { let gets = 0; let puts = 0; let current = publicEvent(); const pod = { 'pod-resources.get': () => ({ status: 200, body: current }), 'pod-permissions.get': () => (gets++ === 0 ? false : OWNER_SHARE), // the owner already shared it publicly 'pod-permissions.add': () => true, 'app.get': () => ({ id: APP }), 'signature.proxy.query': () => { puts++; return { status: 204 }; } }; const w = service(pod, { granted: [] }); await w.svc.onUpdate(w.ctx, E, ALICE); // ACL unreadable: no grant, no record expect(w.ledger.has(E)).toBe(false); current = privateEvent(); await w.svc.onUpdate(w.ctx, E, ALICE); expect(puts).toBe(0); // the owner's public share is left untouched }); }); describe('public-read — a timed-out reconcile writes nothing more (second review)', () => { test('a stale "public" reconcile stalled in its ACL read neither records nor grants after a newer "private" one', async () => { let reads = 0; let aclGets = 0; let aclPublic = false; const pod = { 'pod-resources.get': () => ({ status: 200, body: reads++ === 0 ? publicEvent() : privateEvent() }), 'pod-permissions.get': async () => { if (aclGets++ === 0) await new Promise(resolve => setTimeout(resolve, 120)); // stalls past the timeout return aclPublic ? [DEFAULT_READ, PUBLIC_READ_AUTH] : [DEFAULT_READ]; }, 'pod-permissions.add': () => { aclPublic = true; return true; } }; const w = service(pod, { granted: [], timeoutMs: 50 }); const stale = w.svc.reconcileLogged(w.ctx, E, ALICE); // reads: public await new Promise(resolve => setTimeout(resolve, 10)); const newer = w.svc.reconcileLogged(w.ctx, E, ALICE); // reads: private (the owner's newer edit) await Promise.all([stale, newer]); await new Promise(resolve => setTimeout(resolve, 250)); // the stale one resumes; its re-decision runs expect(aclPublic).toBe(false); expect(w.ledger.has(E)).toBe(false); }); test('a stale "private" reconcile stalled in its ACL read does not revoke after a newer "public" one', async () => { let reads = 0; let aclGets = 0; let aclPublic = true; const pod = { 'pod-resources.get': () => ({ status: 200, body: reads++ === 0 ? privateEvent() : publicEvent() }), 'pod-permissions.get': async () => { if (aclGets++ === 0) await new Promise(resolve => setTimeout(resolve, 120)); return aclPublic ? [DEFAULT_READ, PUBLIC_READ_AUTH] : [DEFAULT_READ]; }, 'pod-permissions.add': () => { aclPublic = true; return true; }, 'app.get': () => ({ id: APP }), 'signature.proxy.query': () => { aclPublic = false; return { status: 204 }; } }; const w = service(pod, { granted: [E], timeoutMs: 50 }); const stale = w.svc.reconcileLogged(w.ctx, E, ALICE); // reads: private await new Promise(resolve => setTimeout(resolve, 10)); const newer = w.svc.reconcileLogged(w.ctx, E, ALICE); // reads: public await Promise.all([stale, newer]); await new Promise(resolve => setTimeout(resolve, 250)); expect(aclPublic).toBe(true); }); test('a grant that lands AFTER its timeout is undone by the re-decision that follows', async () => { let current = publicEvent(); let aclPublic = false; let adds = 0; const pod = { 'pod-resources.get': () => ({ status: 200, body: current }), 'pod-permissions.get': () => (aclPublic ? [DEFAULT_READ, PUBLIC_READ_AUTH] : [DEFAULT_READ]), 'pod-permissions.add': async () => { if (adds++ === 0) await new Promise(resolve => setTimeout(resolve, 120)); // sent in time, lands late aclPublic = true; return true; }, 'app.get': () => ({ id: APP }), 'signature.proxy.query': () => { aclPublic = false; return { status: 204 }; } }; const w = service(pod, { granted: [], timeoutMs: 50 }); const stale = w.svc.reconcileLogged(w.ctx, E, ALICE); await new Promise(resolve => setTimeout(resolve, 60)); // timed out with its PATCH in flight current = privateEvent(); const newer = w.svc.reconcileLogged(w.ctx, E, ALICE); // runs before the late PATCH lands await Promise.all([stale, newer]); await new Promise(resolve => setTimeout(resolve, 250)); expect(aclPublic).toBe(false); }); }); describe('public-read — late-settle handling only after a late WRITE, and never chained (round-4 review)', () => { test('a slow pod and NO late write: a timed-out reconcile schedules nothing', async () => { let reads = 0; const pod = { 'pod-resources.get': async () => { reads++; await new Promise(resolve => setTimeout(resolve, 120)); return { status: 200, body: publicEvent() }; }, 'pod-permissions.get': () => [DEFAULT_READ, PUBLIC_READ_AUTH] }; const w = service(pod, { granted: [], timeoutMs: 50 }); await w.svc.onUpdate(w.ctx, E, ALICE); await new Promise(resolve => setTimeout(resolve, 1000)); expect(reads).toBe(1); }); test('a re-decision after a late write schedules no other, even when it writes late itself', async () => { let reads = 0; let aclPublic = false; const slow = () => new Promise(resolve => setTimeout(resolve, 120)); const pod = { // The owner keeps flipping the event: every read sees the other visibility. 'pod-resources.get': () => ({ status: 200, body: reads++ % 2 === 0 ? publicEvent() : privateEvent() }), 'pod-permissions.get': () => (aclPublic ? [DEFAULT_READ, PUBLIC_READ_AUTH] : [DEFAULT_READ]), 'pod-permissions.add': async () => { await slow(); // every write lands after the limit aclPublic = true; return true; }, 'app.get': () => ({ id: APP }), 'signature.proxy.query': async () => { await slow(); aclPublic = false; return { status: 204 }; } }; const w = service(pod, { granted: [], timeoutMs: 50 }); await w.svc.onUpdate(w.ctx, E, ALICE); await new Promise(resolve => setTimeout(resolve, 1200)); expect(reads).toBe(2); // the reconcile and its ONE re-decision — no chain }); });