| 1 | // De noodknop is niet door een vreemde uit te zetten (shaer-gt70).
|
|---|
| 2 | //
|
|---|
| 3 | // De markering op een hulpvraag ('ik kijk ernaar' / 'afgehandeld') werd van
|
|---|
| 4 | // IEDEREEN aangenomen: de enige voorwaarde was dat de actor niet lokaal is.
|
|---|
| 5 | // Ondertekening zegt WIE, niet OF HET MAG. Afgehandeld kent geen terugdraai en
|
|---|
| 6 | // daarna verdwijnt de vraag uit de teller van elke guardian, dus een vreemde die
|
|---|
| 7 | // de URI kende kon de noodknop van een kind uitzetten.
|
|---|
| 8 | //
|
|---|
| 9 | // De regel die er nu staat: de ward is de bron van waarheid over wie zijn
|
|---|
| 10 | // guardians zijn, en WELKE ward erbij hoort komt uit onze eigen administratie.
|
|---|
| 11 | import { test } from 'node:test';
|
|---|
| 12 | import assert from 'node:assert/strict';
|
|---|
| 13 |
|
|---|
| 14 | process.env.DATABASE_PATH = ':memory:';
|
|---|
| 15 | process.env.PUBLIC_BASE_URL = 'https://test.example';
|
|---|
| 16 |
|
|---|
| 17 | const dbMod = await import('../src/config/database.js');
|
|---|
| 18 | const db = dbMod.default;
|
|---|
| 19 | { const stil = console.log; console.log = () => {}; try { dbMod.initializeDatabase(); } finally { console.log = stil; } }
|
|---|
| 20 | const AP = (await import('../src/services/ActivityPubService.js')).default;
|
|---|
| 21 | const help = (await import('../src/services/guardianship/help.js')).default;
|
|---|
| 22 |
|
|---|
| 23 | // Het kind woont hier; wij zijn zijn guardian-instantie en hebben zijn
|
|---|
| 24 | // hulpvraag binnengekregen. Een tweede guardian woont elders.
|
|---|
| 25 | db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)').run('u1', 'u1', 'u1@t', 'x', 'god');
|
|---|
| 26 | db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_primary) VALUES (?,?,?,?,1)').run('s1', 'kid', 'Kid', 'u1');
|
|---|
| 27 | db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run('s2', 'gua', 'Guardian', 'u1');
|
|---|
| 28 |
|
|---|
| 29 | const WARD = 'https://test.example/ap/users/kid';
|
|---|
| 30 | const MEDE_GUARDIAN = 'https://elders.example/u/oma';
|
|---|
| 31 | const VREEMDE = 'https://elders.example/u/vreemde';
|
|---|
| 32 | const HULPVRAAG = 'https://test.example/ap/notes/hulp-1';
|
|---|
| 33 |
|
|---|
| 34 | // De ward kent zijn guardians: dat is de lijst waar het antwoord vandaan komt.
|
|---|
| 35 | db.prepare("INSERT INTO ap_guardianships (slug, role, other_uri, status) VALUES ('kid','ward',?,'accepted')").run(MEDE_GUARDIAN);
|
|---|
| 36 | // En wij hebben de hulpvraag echt binnengekregen.
|
|---|
| 37 | db.prepare(`INSERT INTO ap_mentions (slug, object_uri, actor_uri, content, help_request)
|
|---|
| 38 | VALUES ('gua', ?, ?, '<p>help</p>', 1)`).run(HULPVRAAG, WARD);
|
|---|
| 39 |
|
|---|
| 40 | /** Een markering zoals hij over de lijn komt, van `van`. */
|
|---|
| 41 | async function markeer(van, noteUri = HULPVRAAG, kind = 'handled') {
|
|---|
| 42 | const note = {
|
|---|
| 43 | id: `${van}/markering/${Math.random().toString(36).slice(2)}`,
|
|---|
| 44 | type: 'Note', attributedTo: van, to: ['https://test.example/ap/users/gua'],
|
|---|
| 45 | inReplyTo: noteUri,
|
|---|
| 46 | [kind === 'handled' ? 'shaer:helpHandled' : 'shaer:helpPickup']: noteUri,
|
|---|
| 47 | content: '<p>markering</p>',
|
|---|
| 48 | tag: [{ type: 'Mention', href: 'https://test.example/ap/users/gua', name: '@gua@test.example' }],
|
|---|
| 49 | };
|
|---|
| 50 | const act = { '@context': 'https://www.w3.org/ns/activitystreams', id: `${note.id}#create`, type: 'Create', actor: van, object: note };
|
|---|
| 51 | const stil = console.warn; console.warn = () => {};
|
|---|
| 52 | try {
|
|---|
| 53 | await AP.handleInbox(
|
|---|
| 54 | { body: act, headers: {}, method: 'POST', originalUrl: '/ap/users/gua/inbox', rawBody: Buffer.from(JSON.stringify(act)) },
|
|---|
| 55 | 'gua', { id: van },
|
|---|
| 56 | );
|
|---|
| 57 | } finally { console.warn = stil; }
|
|---|
| 58 | }
|
|---|
| 59 |
|
|---|
| 60 | const staat = () => help.statusOf(HULPVRAAG);
|
|---|
| 61 |
|
|---|
| 62 | test('een vreemde krijgt de noodknop niet uit', async () => {
|
|---|
| 63 | await markeer(VREEMDE);
|
|---|
| 64 | assert.equal(staat().handled, null, 'een vreemde mag een hulpvraag niet afsluiten');
|
|---|
| 65 | assert.equal(staat().open, true, 'en de vraag staat dus nog open');
|
|---|
| 66 | });
|
|---|
| 67 |
|
|---|
| 68 | test('een guardian van deze ward mag het wel', async () => {
|
|---|
| 69 | await markeer(MEDE_GUARDIAN);
|
|---|
| 70 | assert.ok(staat().handled, 'een mede-guardian sluit hem wel af');
|
|---|
| 71 | });
|
|---|
| 72 |
|
|---|
| 73 | test('een markering voor een hulpvraag die wij niet hebben, telt niet', async () => {
|
|---|
| 74 | const ONBEKEND = 'https://elders.example/n/ergens-anders-gezien';
|
|---|
| 75 | await markeer(MEDE_GUARDIAN, ONBEKEND, 'pickup');
|
|---|
| 76 | assert.equal(help.statusOf(ONBEKEND).pickedUpBy.length, 0,
|
|---|
| 77 | 'zonder eigen rij is er niets te markeren -- de note-URI komt van de afzender');
|
|---|
| 78 | });
|
|---|
| 79 |
|
|---|
| 80 | test('de verwerking valt niet om op de markering zelf', async () => {
|
|---|
| 81 | // Tweede vondst bij deze bead, gemeten: de markeerweg riep wakeGuardian(slug)
|
|---|
| 82 | // aan met een `slug` die in die scope niet bestaat. Gevolg: de markering werd
|
|---|
| 83 | // vastgelegd en daarna gooide de handler een ReferenceError -- het paneel
|
|---|
| 84 | // hoorde het nooit en de rest van de verwerking van die activiteit viel weg.
|
|---|
| 85 | // Deze toets faalt zodra iemand daar weer een naam neerzet die er niet is.
|
|---|
| 86 | const DERDE = 'https://test.example/ap/notes/hulp-3';
|
|---|
| 87 | db.prepare(`INSERT INTO ap_mentions (slug, object_uri, actor_uri, content, help_request)
|
|---|
| 88 | VALUES ('gua', ?, ?, '<p>help</p>', 1)`).run(DERDE, WARD);
|
|---|
| 89 | const note = {
|
|---|
| 90 | id: `${MEDE_GUARDIAN}/markering/heel`, type: 'Note', attributedTo: MEDE_GUARDIAN,
|
|---|
| 91 | to: ['https://test.example/ap/users/gua'], 'shaer:helpPickup': DERDE, content: '<p>ik kijk</p>',
|
|---|
| 92 | tag: [{ type: 'Mention', href: 'https://test.example/ap/users/gua', name: '@gua@test.example' }],
|
|---|
| 93 | };
|
|---|
| 94 | const act = { id: `${note.id}#create`, type: 'Create', actor: MEDE_GUARDIAN, object: note };
|
|---|
| 95 | const status = await AP.handleInbox(
|
|---|
| 96 | { body: act, headers: {}, method: 'POST', originalUrl: '/ap/users/gua/inbox', rawBody: Buffer.from(JSON.stringify(act)) },
|
|---|
| 97 | 'gua', { id: MEDE_GUARDIAN },
|
|---|
| 98 | );
|
|---|
| 99 | assert.equal(status, 202, 'de inbox hoort netjes 202 te geven, niet te gooien');
|
|---|
| 100 | assert.equal(help.statusOf(DERDE).pickedUpBy.length, 1, 'en de oppik is vastgelegd');
|
|---|
| 101 | });
|
|---|
| 102 |
|
|---|
| 103 | test('guardian van een ANDERE ward is hier geen guardian', async () => {
|
|---|
| 104 | // Iemand kan een echte guardian zijn, maar van een ander kind. De vraag is
|
|---|
| 105 | // niet "is dit een guardian" maar "is dit een guardian van DEZE ward".
|
|---|
| 106 | db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run('s3', 'kid2', 'Kid2', 'u1');
|
|---|
| 107 | const ANDERE = 'https://elders.example/u/opa';
|
|---|
| 108 | db.prepare("INSERT INTO ap_guardianships (slug, role, other_uri, status) VALUES ('kid2','ward',?,'accepted')").run(ANDERE);
|
|---|
| 109 | const TWEEDE = 'https://test.example/ap/notes/hulp-2';
|
|---|
| 110 | db.prepare(`INSERT INTO ap_mentions (slug, object_uri, actor_uri, content, help_request)
|
|---|
| 111 | VALUES ('gua', ?, ?, '<p>help</p>', 1)`).run(TWEEDE, WARD);
|
|---|
| 112 | await markeer(ANDERE, TWEEDE);
|
|---|
| 113 | assert.equal(help.statusOf(TWEEDE).handled, null, 'guardian van een ander kind blijft hier een vreemde');
|
|---|
| 114 | });
|
|---|