| 1 | // Een Move is een Move: de guardian blijft dezelfde guardian.
|
|---|
| 2 | //
|
|---|
| 3 | // Toezicht is een gewone Follow (FEP-633c §5), en die verhuisde al mee. Maar de
|
|---|
| 4 | // guardianship-RELATIE bleef aan de oude URI hangen. Dat gaf de vervelendste
|
|---|
| 5 | // toestand van allemaal: de guardian ziet de posts van het kind gewoon
|
|---|
| 6 | // binnenkomen, terwijl alles dat op other_uri matcht omvalt. Half een vangnet
|
|---|
| 7 | // ziet eruit als een heel vangnet, en dan merk je het pas als het nodig is.
|
|---|
| 8 | import { test, beforeEach } from 'node:test';
|
|---|
| 9 | import assert from 'node:assert/strict';
|
|---|
| 10 |
|
|---|
| 11 | process.env.DATABASE_PATH = ':memory:';
|
|---|
| 12 | process.env.PUBLIC_BASE_URL = 'https://ons.example';
|
|---|
| 13 |
|
|---|
| 14 | const dbMod = await import('../src/config/database.js');
|
|---|
| 15 | const db = dbMod.default;
|
|---|
| 16 | {
|
|---|
| 17 | const stil = console.log;
|
|---|
| 18 | console.log = () => {};
|
|---|
| 19 | try { dbMod.initializeDatabase(); } finally { console.log = stil; }
|
|---|
| 20 | }
|
|---|
| 21 | const AP = await import('../src/services/ActivityPubService.js');
|
|---|
| 22 |
|
|---|
| 23 | const OUD = 'https://oud.example/ap/users/kind';
|
|---|
| 24 | const NIEUW = 'https://nieuw.example/ap/users/kind';
|
|---|
| 25 |
|
|---|
| 26 | // De guardian zijn WIJ: een lokale site die het kind volgt en de relatie draagt.
|
|---|
| 27 | function zetKlaar({ status = 'accepted' } = {}) {
|
|---|
| 28 | db.prepare('DELETE FROM ap_following').run();
|
|---|
| 29 | db.prepare('DELETE FROM ap_guardianships').run();
|
|---|
| 30 | db.prepare('INSERT OR IGNORE INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
|
|---|
| 31 | .run('u1', 'u1', 'u1@test', 'x', 'god');
|
|---|
| 32 | db.prepare('INSERT OR IGNORE INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)')
|
|---|
| 33 | .run('s1', 'voogd', 'De voogd', 'u1');
|
|---|
| 34 | db.prepare(`INSERT INTO ap_following (slug, actor_uri, handle, status, auto_boost)
|
|---|
| 35 | VALUES (?,?,?,?,?)`).run('voogd', OUD, '@kind@oud.example', 'accepted', 1);
|
|---|
| 36 | db.prepare(`INSERT INTO ap_guardianships (slug, other_uri, role, status)
|
|---|
| 37 | VALUES (?,?,?,?)`).run('voogd', OUD, 'guardian', status);
|
|---|
| 38 | }
|
|---|
| 39 |
|
|---|
| 40 | const relatie = () => db.prepare('SELECT other_uri, role, status FROM ap_guardianships').all();
|
|---|
| 41 |
|
|---|
| 42 | // Geen netwerk: de Move-afhandeling accepteert injecteerbare functies.
|
|---|
| 43 | const stubs = {
|
|---|
| 44 | verifiedActor: OUD,
|
|---|
| 45 | fetchActorFn: async (uri) => ({ id: uri, inbox: `${uri}/inbox`, alsoKnownAs: [OUD] }),
|
|---|
| 46 | followFn: async () => true,
|
|---|
| 47 | unfollowFn: async () => true,
|
|---|
| 48 | };
|
|---|
| 49 | const move = { type: 'Move', actor: OUD, object: OUD, target: NIEUW };
|
|---|
| 50 |
|
|---|
| 51 | beforeEach(() => zetKlaar());
|
|---|
| 52 |
|
|---|
| 53 | test('na een Move wijst de guardianship naar het NIEUWE adres', async () => {
|
|---|
| 54 | const stil = console.log; console.log = () => {};
|
|---|
| 55 | try { await AP.handleMoveInbox(move, stubs); } finally { console.log = stil; }
|
|---|
| 56 |
|
|---|
| 57 | const r = relatie();
|
|---|
| 58 | assert.equal(r.length, 1, 'de relatie mag niet verdubbelen of verdwijnen');
|
|---|
| 59 | assert.equal(r[0].other_uri, NIEUW, 'anders ziet de guardian de posts wel, maar valt de gate om');
|
|---|
| 60 | assert.equal(r[0].role, 'guardian', 'de rol verandert niet: hij is dezelfde guardian');
|
|---|
| 61 | assert.equal(r[0].status, 'accepted');
|
|---|
| 62 | });
|
|---|
| 63 |
|
|---|
| 64 | test('de Follow verhuist mee, met de uitgelicht-stand', async () => {
|
|---|
| 65 | const gevolgd = [];
|
|---|
| 66 | const stil = console.log; console.log = () => {};
|
|---|
| 67 | try {
|
|---|
| 68 | await AP.handleMoveInbox(move, {
|
|---|
| 69 | ...stubs,
|
|---|
| 70 | followFn: async (site, uri, boost) => { gevolgd.push([uri, boost]); return true; },
|
|---|
| 71 | });
|
|---|
| 72 | } finally { console.log = stil; }
|
|---|
| 73 | assert.deepEqual(gevolgd, [[NIEUW, true]]);
|
|---|
| 74 | });
|
|---|
| 75 |
|
|---|
| 76 | test('een niet-geverifieerde Move raakt de guardianship niet aan', async () => {
|
|---|
| 77 | const stil = console.warn; console.warn = () => {};
|
|---|
| 78 | try {
|
|---|
| 79 | // verifiedActor wijst niet naar de oude actor: dit is niet zijn Move.
|
|---|
| 80 | await AP.handleMoveInbox(move, { ...stubs, verifiedActor: 'https://iemand.anders/ap/users/x' });
|
|---|
| 81 | } finally { console.warn = stil; }
|
|---|
| 82 | assert.equal(relatie()[0].other_uri, OUD,
|
|---|
| 83 | 'anders kan een vreemde het kind bij zijn guardians weghalen door een Move te sturen');
|
|---|
| 84 | });
|
|---|
| 85 |
|
|---|
| 86 | test('een relatie die nog niet geaccepteerd is blijft staan', async () => {
|
|---|
| 87 | zetKlaar({ status: 'pending' });
|
|---|
| 88 | const stil = console.log; console.log = () => {};
|
|---|
| 89 | try { await AP.handleMoveInbox(move, stubs); } finally { console.log = stil; }
|
|---|
| 90 | assert.equal(relatie()[0].other_uri, OUD,
|
|---|
| 91 | 'een verzoek dat nog loopt gaat over de OUDE actor; dat verhuizen zou een niet-bestaande relatie meenemen');
|
|---|
| 92 | });
|
|---|
| 93 |
|
|---|
| 94 | // De VOLGORDE, niet de uitkomst. De tests hierboven slagen ook als de relatie pas
|
|---|
| 95 | // na de follows wordt bijgewerkt, en juist dat ging mis bij Robins verhuizing:
|
|---|
| 96 | //
|
|---|
| 97 | // [AP] outgoing Follow beta → .../robo (gated, awaiting guardians)
|
|---|
| 98 | //
|
|---|
| 99 | // Beta is zelf een ward, dus zijn uitgaande follow naar de verhuisde guardian werd
|
|---|
| 100 | // gepoort omdat het nieuwe adres nog niet in zijn guardian-lijst stond. Beide
|
|---|
| 101 | // richtingen bleven hangen op een goedkeuring die niemand hoefde te geven.
|
|---|
| 102 | test('de guardianship is al bijgewerkt VOORDAT de follow uitgaat', async () => {
|
|---|
| 103 | let standTijdensFollow = null;
|
|---|
| 104 | const stil = console.log; console.log = () => {};
|
|---|
| 105 | try {
|
|---|
| 106 | await AP.handleMoveInbox(move, {
|
|---|
| 107 | ...stubs,
|
|---|
| 108 | followFn: async () => {
|
|---|
| 109 | // Op dit moment moet de gate aan de andere kant ons al kennen.
|
|---|
| 110 | standTijdensFollow = db.prepare('SELECT other_uri FROM ap_guardianships').get().other_uri;
|
|---|
| 111 | return true;
|
|---|
| 112 | },
|
|---|
| 113 | });
|
|---|
| 114 | } finally { console.log = stil; }
|
|---|
| 115 |
|
|---|
| 116 | assert.equal(standTijdensFollow, NIEUW,
|
|---|
| 117 | 'staat de relatie hier nog op het oude adres, dan gate\'t de ward de follow van zijn eigen guardian');
|
|---|
| 118 | });
|
|---|
| 119 |
|
|---|
| 120 | test('een Move naar een geblokkeerde bestemming verandert niets', async () => {
|
|---|
| 121 | db.prepare('INSERT INTO ap_blocks (slug, target, kind) VALUES (?,?,?)').run('voogd', NIEUW, 'actor');
|
|---|
| 122 | const stil = console.log; console.log = () => {};
|
|---|
| 123 | try { await AP.handleMoveInbox(move, stubs); } finally { console.log = stil; }
|
|---|
| 124 | db.prepare('DELETE FROM ap_blocks').run();
|
|---|
| 125 | assert.equal(relatie()[0].other_uri, OUD, 'geblokkeerd is geblokkeerd, ook voor een guardianship');
|
|---|
| 126 | });
|
|---|