| 1 | // FEP-633c §5.3: follow-gating store. A follow on a ward waits for guardians.
|
|---|
| 2 | import { test } from 'node:test';
|
|---|
| 3 | import assert from 'node:assert/strict';
|
|---|
| 4 |
|
|---|
| 5 | process.env.DATABASE_PATH = ':memory:';
|
|---|
| 6 | process.env.PUBLIC_BASE_URL = 'https://test.example';
|
|---|
| 7 |
|
|---|
| 8 | const dbMod = await import('../src/config/database.js');
|
|---|
| 9 | dbMod.initializeDatabase();
|
|---|
| 10 | const G = await import('../src/services/guardianship/index.js');
|
|---|
| 11 |
|
|---|
| 12 | const A = (s) => `https://test.example/ap/users/${s}`;
|
|---|
| 13 | const [KID, MOM, DAD, STRANGER] = [A('kid'), A('mom'), A('dad'), A('stranger')];
|
|---|
| 14 |
|
|---|
| 15 | test('any-quorum: one guardian approval is enough', () => {
|
|---|
| 16 | G.follows.recordPending('kid', { id: 'f1', follower: STRANGER, inbox: `${STRANGER}/inbox`, quorum: 'any' });
|
|---|
| 17 | assert.equal(G.follows.listForWard('kid').length, 1);
|
|---|
| 18 | const r = G.follows.decide('f1', MOM, 'approve', [MOM, DAD]);
|
|---|
| 19 | assert.equal(r.outcome, 'approved');
|
|---|
| 20 | G.follows.remove('f1');
|
|---|
| 21 | assert.equal(G.follows.listForWard('kid').length, 0);
|
|---|
| 22 | });
|
|---|
| 23 |
|
|---|
| 24 | test('all-quorum: needs every guardian', () => {
|
|---|
| 25 | G.follows.recordPending('kid', { id: 'f2', follower: STRANGER, inbox: `${STRANGER}/inbox`, quorum: 'all' });
|
|---|
| 26 | assert.equal(G.follows.decide('f2', MOM, 'approve', [MOM, DAD]).outcome, 'waiting');
|
|---|
| 27 | assert.equal(G.follows.decide('f2', DAD, 'approve', [MOM, DAD]).outcome, 'approved');
|
|---|
| 28 | });
|
|---|
| 29 |
|
|---|
| 30 | test('a single reject denies the follow (§3.2 spirit)', () => {
|
|---|
| 31 | G.follows.recordPending('kid', { id: 'f3', follower: STRANGER, inbox: `${STRANGER}/inbox`, quorum: 'any' });
|
|---|
| 32 | const r = G.follows.decide('f3', DAD, 'reject', [MOM, DAD]);
|
|---|
| 33 | assert.equal(r.outcome, 'rejected');
|
|---|
| 34 | });
|
|---|
| 35 |
|
|---|
| 36 | test('guardian-side review copy (cross-instance, modelled on the offer)', () => {
|
|---|
| 37 | // A remote ward's server forwarded an Offer(Follow); the guardian stores a copy.
|
|---|
| 38 | G.follows.recordReview('gran', { id: 'r1', wardUri: 'https://w.example/kid', wardInbox: 'https://w.example/kid/inbox', follower: STRANGER, followerHandle: '@x@m.social' });
|
|---|
| 39 | const list = G.follows.listReviews('gran');
|
|---|
| 40 | assert.equal(list.length, 1);
|
|---|
| 41 | assert.equal(list[0].ward_inbox, 'https://w.example/kid/inbox');
|
|---|
| 42 | assert.ok(G.follows.getReview('gran', 'r1'));
|
|---|
| 43 | G.follows.removeReview('gran', 'r1');
|
|---|
| 44 | assert.equal(G.follows.listReviews('gran').length, 0);
|
|---|
| 45 | });
|
|---|