morgana: row 275 r3 (duckworth-0166): B1 bob asks create only, open and send on alice's own thread; N1 thread B holds a message and the viewer read asserts its id

This commit is contained in:
jason.woltje committed 2026-10-22 12:46:09 -05:00
1 parent 1e3db53436
commit 3410df7c3b
2 files changed
+11 -7

No files matched your search

+4 -2
View File
@@ -59,10 +59,12 @@ No other test names a viewer grant on the bus (git grep for `level: 'viewer'` ou
2. A viewer binds; project.list, names, thread.list and thread.messages answer what its grant covers
(alice downgraded, bob viewer from the start).
3. A viewer is refused thread.create, thread.open and thread.send `access-refused`, and messages, threads
and thread_opens counts are unchanged. Kills mutant 6.
and thread_opens counts are unchanged. Kills mutant 6 at alice's send. r3 (duckworth-0166 B1): bob is
asked create only, as `#thread` refuses a non-owner `thread-not-owned` before the level check; open and
send are asked on alice's own thread. Bob stays the viewer who never held a collaborator grant.
4. Every verb off USER_VERBS refuses a viewer `access-refused` before its own checks.
5. Mixed grants: collaborator on project a, viewer on b. Create in a works, create in b refuses; send in b's
old thread refuses, open refuses, messages reads.
old thread refuses, open refuses, messages reads the message sent while collaborator (r3, N1).
6. Board reads (BOARD_VERBS, the project file verbs, limits, plugins, instances, routes) refuse alice
(downgraded viewer), bob (viewer) and carol (collaborator) `access-refused`. Kills mutant 10.
7. Governance pin, user side: one row per item of the 5.7 sentence (decisions, inbox entries, claims as
+7 -5
View File
@@ -102,11 +102,12 @@ test('275: a viewer is refused thread.create, thread.open and thread.send access
const { store, code, p1, A } = viewers(t);
const counts = () => ['messages', 'threads', 'thread_opens'].map((x) => store.get(`SELECT count(*) n FROM ${x}`).n);
const before = counts();
for (const who of ['alice', 'bob']) {
for (const who of ['alice', 'bob'])
assert.equal(code(who, 'thread.create', { project: p1, name: `${who} new` }), 'access-refused', `${who} create`);
assert.equal(code(who, 'thread.open', { thread: A }), 'access-refused', `${who} open`);
assert.equal(code(who, 'thread.send', { thread: A, body: 'still here?' }), 'access-refused', `${who} send`);
}
// Only alice owns a thread: #thread refuses a non-owner thread-not-owned before the level check
// (duckworth-0166 B1), so open and send are asked on her own A.
assert.equal(code('alice', 'thread.open', { thread: A }), 'access-refused', 'alice open');
assert.equal(code('alice', 'thread.send', { thread: A, body: 'still here?' }), 'access-refused', 'alice send');
assert.deepEqual(counts(), before);
});
@@ -126,6 +127,7 @@ test('275: mixed grants, collaborator on one project and viewer on another, take
access.doc.grants = [grant({ projects: [pa] }), grant({ id: 'g-2', projects: [pb] })];
user('alice');
const B = call('alice', 'thread.create', { project: pb, name: 'b work' }).id;
const inB = call('alice', 'thread.send', { thread: B, body: 'in b, before' }).id;
access.doc.grants = [grant({ projects: [pa] }), grant({ id: 'g-2', projects: [pb], level: 'viewer' })];
const A = call('alice', 'thread.create', { project: pa, name: 'a work' }).id;
assert.ok(A, 'create in the collaborator project works');
@@ -135,7 +137,7 @@ test('275: mixed grants, collaborator on one project and viewer on another, take
assert.ok(call('alice', 'thread.send', { thread: A, body: 'in a' }).id);
assert.equal(code('alice', 'thread.send', { thread: B, body: 'in b' }), 'access-refused');
assert.equal(code('alice', 'thread.open', { thread: B }), 'access-refused');
assert.deepEqual(call('alice', 'thread.messages', { thread: B }), []);
assert.deepEqual(call('alice', 'thread.messages', { thread: B }).map((m) => m.id), [inB]);
});
// Mutant 10 (row 193 design 8): a user allowed `trail`. This test kills it; R3's exact USER_VERBS