source: Klonkt/test/follow-gating.test.js@ 2b4252c

main
Last change on this file since 2b4252c was 2b4252c, checked in by Robin <roboburr@…>, 7 weeks ago

Cross-instance follow-goedkeuring, gemodelleerd op de guardian-offer (§5.3)

Guardians die op een andere instance wonen kunnen nu een gated follow goedkeuren.
Zelfde gedistribueerde patroon als de adoptie-offer: de ward forwardt de follow
naar z'n guardians als een Offer(Follow); elke guardian houdt een kopie en ziet
'm in /guardian2; de guardian antwoordt met Accept/Reject naar de ward; de ward
telt quorum (bestaande follows.decide) en stuurt de gewone Accept(Follow) naar de
volger. Lokale guardians blijven zoals ze waren (push + lokale beslissing, geen
forwarding). Alles achter de shaer:followApproval-marker, dus normaal verkeer
onaangeroerd.

Changed files:
src/services/ActivityPubService.js

  • gate: remote guardian krijgt Offer(Follow) (leg 1); lokale guardian push
  • handleFollowApprovalInbox: Offer(Follow) -> review (leg 2), Accept/Reject -> decide+commit (leg 4)
  • sendFollowDecision: guardian stuurt beslissing naar de ward (leg 3)
  • dispatch-tak op shaer:followApproval vóór de handshake-dispatch

src/services/guardianship/follows.js

  • guardian-side review-store (recordReview/getReview/listReviews/removeReview)

src/config/database.js

  • ap_follow_reviews (PK slug,id): de guardian-kopie van een remote-ward-follow

src/routes/guardian2.js

  • follow-requests toont ook reviews (remote); approve op een review = sendFollowDecision

test/follow-gating.test.js

  • review-store test erbij

remarks: npm test 171/171. Spec-clausule §5.3 toegevoegd (forwarding gemodelleerd
op §3). Lokale/co-located flow ongewijzigd.

-robo
Co-Authored-By: Claude Opus 4.8 <noreply@…>

  • Property mode set to 100644
File size: 2.2 KB
Line 
1// FEP-633c §5.3: follow-gating store. A follow on a ward waits for guardians.
2import { test } from 'node:test';
3import assert from 'node:assert/strict';
4
5process.env.DATABASE_PATH = ':memory:';
6process.env.PUBLIC_BASE_URL = 'https://test.example';
7
8const dbMod = await import('../src/config/database.js');
9dbMod.initializeDatabase();
10const G = await import('../src/services/guardianship/index.js');
11
12const A = (s) => `https://test.example/ap/users/${s}`;
13const [KID, MOM, DAD, STRANGER] = [A('kid'), A('mom'), A('dad'), A('stranger')];
14
15test('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
24test('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
30test('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
36test('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});
Note: See TracBrowser for help on using the repository browser.