source: Klonkt/test/guardian-event-log.test.js@ f06ed56

main
Last change on this file since f06ed56 was f06ed56, checked in by Bart <bart@…>, 4 weeks ago

Een logboek, zodat de reden ergens blijft staan

Guardianship-events waren vluchtig: onGuardianshipEvent wekte de long-poll,
zocht in een tabel van zeven soorten of er een push bij hoorde, en liet de rest
vallen. Elf van de achttien soorten verdwenen spoorloos, met hun inhoud. Een
weigering droeg reason: 'not_a_teapot' tot precies daar en niet verder --
terwijl §4.2 eist dat de ward en zijn guardians die reden te HOREN krijgen, en
niet dat ze hem afleiden uit een aanbod dat opeens weg is.

Vastleggen en melden zijn nu twee dingen. Alles komt in ap_guardian_events;
welke gebeurtenis een mens wakker maakt blijft de aparte, korte lijst die het
altijd al was.

Ingeklapt en onderaan, want hier vraagt niets om een antwoord. Zou dit tussen
de wachtrijen staan, dan wordt "moet ik iets doen" onleesbaar -- dezelfde reden
waarom de afgehandelde hulpvragen daar al staan.

200 per account, jongste eerst. Een logboek dat oneindig groeit wordt er een
die niemand opent, en afkappen aan de verkeerde kant zou hem onbruikbaar maken
op het moment dat er juist iets gebeurt. Schrijffouten worden geslikt: het
logboek is bijzaak en mag de commit of de weigering zelf nooit meesleuren.

Onbekende soorten vallen in de PWA terug op hun ruwe naam. Zichtbaar en lelijk
is beter dan netjes en afwezig.

Co-Authored-By: Claude Opus 5 <claude@…>

  • Property mode set to 100644
File size: 3.8 KB
Line 
1// FEP-633c §4.2 — de reden mag niet verdampen.
2//
3// Guardianship-events waren vluchtig: onGuardianshipEvent wekte de long-poll,
4// keek in een tabel van zeven soorten of er een push bij hoorde, en liet de
5// rest vallen. Elf van de achttien soorten verdwenen daarmee spoorloos, met hun
6// inhoud erbij. Een weigering droeg `reason: 'not_a_teapot'` tot precies daar en
7// niet verder, terwijl §4.2 eist dat de ward en zijn guardians die reden TE
8// HOREN krijgen -- en niet dat ze het afleiden uit een aanbod dat opeens weg is.
9//
10// Vastleggen en melden zijn nu twee dingen. Deze test bewaakt het eerste; welke
11// gebeurtenis een mens wakker maakt blijft een aparte beslissing.
12import { test } from 'node:test';
13import assert from 'node:assert/strict';
14
15process.env.DATABASE_PATH = ':memory:';
16process.env.PUBLIC_BASE_URL = 'https://test.example';
17
18const dbMod = await import('../src/config/database.js');
19const db = dbMod.default;
20dbMod.initializeDatabase();
21const AP = (await import('../src/services/ActivityPubService.js')).default;
22
23db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
24 .run('u1', 'u1', 'u1@test', 'x', 'god');
25db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_primary) VALUES (?,?,?,?,1)')
26 .run('s1', 'kid', 'kid', 'u1');
27
28test('een gebeurtenis die geen push oplevert wordt tóch onthouden', () => {
29 // offer_rejected staat niet in de meldingstabel: niemand wordt er wakker van,
30 // en dat was precies de reden dat hij ook nergens terechtkwam.
31 const push = AP.onGuardianshipEvent('kid', {
32 kind: 'offer_rejected', offer: 'o1',
33 reason: 'not_a_teapot', candidate: 'https://elders.example/users/tess',
34 });
35 assert.equal(push, null, 'geen melding, zoals bedoeld');
36
37 const log = AP.listGuardianEvents('kid');
38 assert.equal(log.length, 1, 'maar wel vastgelegd');
39 assert.equal(log[0].kind, 'offer_rejected');
40 assert.equal(log[0].reason, 'not_a_teapot', 'MET de reden: daar staat dit logboek voor');
41 assert.equal(log[0].candidate, 'https://elders.example/users/tess', 'en om wie het ging');
42});
43
44test('nieuwste eerst, want geschiedenis leest van achteren', () => {
45 AP.onGuardianshipEvent('kid', { kind: 'committed', ward: 'https://test.example/ap/users/kid' });
46 const log = AP.listGuardianEvents('kid');
47 assert.equal(log[0].kind, 'committed');
48 assert.equal(log[1].kind, 'offer_rejected');
49});
50
51test('het logboek van een ander account blijft van hem', () => {
52 db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_primary) VALUES (?,?,?,?,0)')
53 .run('s2', 'mum', 'mum', 'u1');
54 assert.deepEqual(AP.listGuardianEvents('mum'), [],
55 'wat er over een kind is besloten is niet van iedereen');
56});
57
58test('het loopt niet vol', () => {
59 // Een logboek dat oneindig groeit wordt een logboek dat niemand opent, en
60 // hier ligt het in dezelfde database als alles wat wél om aandacht vraagt.
61 for (let i = 0; i < AP.GUARDIAN_EVENT_KEEP + 25; i++) {
62 AP.onGuardianshipEvent('mum', { kind: 'setting', n: i });
63 }
64 const n = db.prepare('SELECT COUNT(*) AS n FROM ap_guardian_events WHERE slug = ?').get('mum').n;
65 assert.equal(n, AP.GUARDIAN_EVENT_KEEP, 'afgekapt op de bewaargrens');
66 // En het zijn de JONGSTE die blijven: afkappen aan de verkeerde kant zou het
67 // logboek precies onbruikbaar maken op het moment dat er iets gebeurt.
68 assert.equal(AP.listGuardianEvents('mum', 1)[0].n, AP.GUARDIAN_EVENT_KEEP + 24);
69});
70
71test('een kapotte gebeurtenis breekt de gebeurtenis zelf niet', () => {
72 // Het logboek is bijzaak. Zou een schrijffout hier de commit of de weigering
73 // meesleuren, dan is de cure erger dan de kwaal.
74 assert.doesNotThrow(() => AP.onGuardianshipEvent('kid', { kind: 'setting', zelf: { a: null } }));
75 assert.doesNotThrow(() => AP.onGuardianshipEvent('kid', {}));
76 assert.doesNotThrow(() => AP.onGuardianshipEvent(null, { kind: 'setting' }));
77});
Note: See TracBrowser for help on using the repository browser.