| 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('een eenvoudige meerderheid beslist: 1 van 2 is genoeg', () => {
|
|---|
| 25 | // WAS: 'all-quorum: needs every guardian'. Barts besluit (8-8) vervangt de
|
|---|
| 26 | // any/all-tweedeling door een meerderheid, en "1/2 is voldoende" waren zijn
|
|---|
| 27 | // woorden. De 'all'-stand die deze toets bewaakte werd nergens gezet -- elke
|
|---|
| 28 | // schrijver gaf 'any' mee -- dus het was een keuze die niemand kon maken.
|
|---|
| 29 | G.follows.recordPending('kid', { id: 'f2', follower: STRANGER, inbox: `${STRANGER}/inbox`, quorum: 'all' });
|
|---|
| 30 | assert.equal(G.follows.decide('f2', MOM, 'approve', [MOM, DAD]).outcome, 'approved');
|
|---|
| 31 | });
|
|---|
| 32 |
|
|---|
| 33 | test('bij drie guardians is een enkele ja NIET meer genoeg', () => {
|
|---|
| 34 | // De andere kant van hetzelfde besluit: tot vandaag stond dit op 'any', en
|
|---|
| 35 | // dan was een enkele ja genoeg hoeveel guardians er ook waren. Voor iedereen
|
|---|
| 36 | // met drie of meer is dit dus een aanscherping.
|
|---|
| 37 | const GRAN = 'https://gran.example/u/gran';
|
|---|
| 38 | G.follows.recordPending('kid', { id: 'f2b', follower: STRANGER, inbox: `${STRANGER}/inbox` });
|
|---|
| 39 | assert.equal(G.follows.decide('f2b', MOM, 'approve', [MOM, DAD, GRAN]).outcome, 'waiting');
|
|---|
| 40 | assert.equal(G.follows.decide('f2b', DAD, 'approve', [MOM, DAD, GRAN]).outcome, 'approved');
|
|---|
| 41 | });
|
|---|
| 42 |
|
|---|
| 43 | test('a single reject denies the follow (§3.2 spirit)', () => {
|
|---|
| 44 | G.follows.recordPending('kid', { id: 'f3', follower: STRANGER, inbox: `${STRANGER}/inbox`, quorum: 'any' });
|
|---|
| 45 | const r = G.follows.decide('f3', DAD, 'reject', [MOM, DAD]);
|
|---|
| 46 | assert.equal(r.outcome, 'rejected');
|
|---|
| 47 | });
|
|---|
| 48 |
|
|---|
| 49 | test('guardian-side review copy (cross-instance, modelled on the offer)', () => {
|
|---|
| 50 | // A remote ward's server forwarded an Offer(Follow); the guardian stores a copy.
|
|---|
| 51 | G.follows.recordReview('gran', { id: 'r1', wardUri: 'https://w.example/kid', wardInbox: 'https://w.example/kid/inbox', follower: STRANGER, followerHandle: '@x@m.social' });
|
|---|
| 52 | const list = G.follows.listReviews('gran');
|
|---|
| 53 | assert.equal(list.length, 1);
|
|---|
| 54 | assert.equal(list[0].ward_inbox, 'https://w.example/kid/inbox');
|
|---|
| 55 | assert.ok(G.follows.getReview('gran', 'r1'));
|
|---|
| 56 | G.follows.removeReview('gran', 'r1');
|
|---|
| 57 | assert.equal(G.follows.listReviews('gran').length, 0);
|
|---|
| 58 | });
|
|---|