source: Klonkt/test/help-c2s.test.js@ abbb72a

main
Last change on this file since abbb72a was c34ef26, checked in by Robin <roboburr@…>, 4 weeks ago

De ouderdom van een oppak haalde de apps niet (shaer-lgo)

Barts besluit was dat "opgepikt" niet vervalt maar zichtbaar VEROUDERT -- het
verschil tussen "er is iemand mee bezig" en "er was ooit iemand mee bezig". Het
guardian-paneel toont dat ook, met een regel zodra het oudste oppakken langer
dan een uur geleden is.

Maar helpCollection gooide het veld weg. helpStatus() rekent oldestPickupAt
netjes uit; de C2S-wachtrij serialiseerde alleen open, handledBy, handledAt,
pickedUpBy en formerWard. In Shaer zag een oppak van vijf minuten er dus uit als
een van vijf dagen -- en de faalstand is hier nou juist "iedereen denkt dat het
geregeld is".

Als TIJDSTIP en niet als leeftijd, om dezelfde reden die in guardian.js staat:
een leeftijd maakt elk antwoord anders en dan kan de ETag nooit gelijk zijn. De
client rekent zelf terug.

En weg zodra hij is afgehandeld: hoe lang er iemand naar keek is dan geen
informatie meer, en zou als "er wacht nog iets" lezen.

Gecontroleerd dat de test bijt: veld eruit -> 7 groen 1 rood, terug -> 8 groen.

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

  • Property mode set to 100644
File size: 5.9 KB
RevLine 
[86e6a45]1// Een hulpvraag oppikken en afsluiten vanuit de app (shaer-lgo, Barts melding 8-8).
2//
3// De apps lazen hulpvragen uit de FEED en kregen de staat niet mee. Ze wisten dus
4// niet of er al iemand op af was, en een afgehandeld verzoek bleef gewoon staan.
5//
6// Wat hier bewaakt wordt: de app loopt door dezelfde markering en dezelfde staat
7// als het paneel. Twee berekeningen zouden twee guardians een ander beeld geven
8// van hetzelfde kind -- en bij een reddingsboei is dat het gevaarlijkste dat er
9// mis kan gaan.
10//
11// In-memory SQLite. Run: npm test
12import { test } from 'node:test';
13import assert from 'node:assert/strict';
14
15process.env.DATABASE_PATH = ':memory:';
16process.env.PUBLIC_BASE_URL = 'https://oma.test';
17
18const dbMod = await import('../src/config/database.js');
19const db = dbMod.default;
20dbMod.initializeDatabase();
21const AP = await import('../src/services/ActivityPubService.js');
22const queues = await import('../src/services/guardianship/queues.js');
23const rel = await import('../src/services/guardianship/relations.js');
24
25db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
26 .run('u1', 'u1', 'u1@t', 'x', 'god');
27db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run('s1', 'oma', 'Oma', 'u1');
28const site = () => db.prepare('SELECT * FROM sites WHERE id = ?').get('s1');
29const user = { id: 'u1', username: 'u1' };
30const KIND = 'https://elders.test/ap/users/kind';
31const BOEI = 'https://elders.test/ap/notes/boei-1';
32rel.commitWardForGuardian('oma', KIND, { handle: '@kind' });
33db.prepare(`INSERT INTO ap_mentions (slug, object_uri, actor_uri, actor_handle, content, help_request)
34 VALUES (?,?,?,?,?,1)`).run('oma', BOEI, KIND, '@kind', '<p>help</p>');
35
36const marker = (kind) => ({
37 type: 'Create',
38 object: {
39 type: 'Note',
40 content: '<p>.</p>',
41 to: [KIND],
42 [kind === 'handled' ? 'shaer:helpHandled' : 'shaer:helpPickup']: BOEI,
43 },
44});
45
46test('een verse hulpvraag staat OPEN', () => {
47 const h = queues.helpItemsFor('oma').find((x) => x.object_uri === BOEI);
48 assert.equal(h.state.open, true);
49 assert.equal(h.state.handled, null);
50});
51
52test('oppikken vanuit de app landt in dezelfde staat als het paneel', async () => {
53 await AP.ingestOutboxActivity(site(), user, marker('pickup'));
54 const h = queues.helpItemsFor('oma').find((x) => x.object_uri === BOEI);
55 assert.equal(h.state.pickedUpBy.length, 1);
56 // Nog steeds open: oppikken is geen afhandelen. De faalstand hier is
57 // "iedereen denkt dat het geregeld is", en die is gevaarlijker dan geen
58 // markering.
59 assert.equal(h.state.open, true);
60});
61
[c34ef26]62test('en de apps krijgen te horen HOE OUD dat oppakken is', () => {
63 // Barts besluit: opgepikt vervalt niet maar veroudert zichtbaar -- het
64 // verschil tussen "er is iemand mee bezig" en "er was ooit iemand mee bezig".
65 // Het paneel toonde dat al uit dezelfde staat; de apps kregen het veld niet,
66 // dus daar zag een oppak van vijf minuten eruit als een van vijf dagen.
67 const coll = queues.helpCollection('https://x/queues/help', 'oma');
68 const item = coll.orderedItems.find((i) => i.id === BOEI);
69 assert.ok(item['shaer:oldestPickupAt'], 'het veld staat er');
70 assert.ok(!Number.isNaN(Date.parse(item['shaer:oldestPickupAt'])), 'en is een leesbaar tijdstip');
71 // Een TIJDSTIP en geen leeftijd: een leeftijd maakt elk antwoord anders en
72 // dan kan de ETag nooit gelijk zijn. De client rekent zelf terug.
73 assert.equal(typeof item['shaer:oldestPickupAt'], 'string');
74});
75
[86e6a45]76test('afsluiten vanuit de app sluit hem ook echt', async () => {
77 await AP.ingestOutboxActivity(site(), user, marker('handled'));
78 const h = queues.helpItemsFor('oma').find((x) => x.object_uri === BOEI);
79 assert.equal(h.state.open, false);
80 assert.ok(h.state.handled, 'met wie het deed erbij');
81});
82
83test('de collectie voor de apps draagt de staat plat mee', () => {
84 // Een app hoeft hem niet af te leiden, en kan hem dus ook niet anders
85 // afleiden dan het paneel.
86 const coll = queues.helpCollection('https://x/queues/help', 'oma');
87 const item = coll.orderedItems.find((i) => i.id === BOEI);
88 assert.equal(item['shaer:open'], false);
89 assert.ok(item['shaer:handledBy']);
90 assert.equal(item['shaer:helpRequest'], true);
[c34ef26]91 // En de ouderdom is WEG zodra hij is afgehandeld: hoe lang er iemand naar
92 // keek is dan geen informatie meer, en zou als "er wacht nog iets" lezen.
93 assert.equal(item['shaer:oldestPickupAt'], undefined);
[86e6a45]94});
95
96test('een note zonder markering blijft een gewoon bericht', async () => {
97 const uit = await AP.ingestOutboxActivity(site(), user, {
98 type: 'Create', object: { type: 'Note', content: '<p>hoi</p>', to: [KIND] },
99 });
100 assert.notEqual(uit.status, 400);
101});
[a9ede28]102
103test('OPEN vragen worden nooit afgekapt, hoeveel het er ook zijn', () => {
104 // Barts punt (8-8): ik noemde tientallen hulpvragen bij een guardian een
105 // randgeval, maar voor een jeugdzorgmedewerker is dat een caseload en dus een
106 // gewone dinsdag. De gebruiker die dit het hardst nodig heeft was precies
107 // degene voor wie het brak.
108 //
109 // Zonder deze regel viel een oudere vraag buiten de queue, vond de app geen
110 // staat, en toonde hem -- terecht, want bij twijfel OPEN -- alsof er nog
111 // iemand op moest.
112 for (let i = 0; i < 120; i++) {
113 db.prepare(`INSERT INTO ap_mentions (slug, object_uri, actor_uri, actor_handle, content, help_request)
114 VALUES (?,?,?,?,?,1)`).run('oma', `https://elders.test/ap/notes/veel-${i}`, KIND, '@kind', '<p>help</p>');
115 }
116 const open = queues.helpItemsFor('oma').filter((h) => h.state.open);
117 assert.equal(open.length, 120, 'alle 120 open vragen komen mee');
118});
119
120test('de collectie zegt dat je uit afwezigheid mag concluderen', () => {
121 // Zonder die vlag mag een app niets afleiden uit een ontbrekende staat, en
122 // valt hij terug op bij-twijfel-open. Dat is de veilige kant, maar dan komt
123 // een afgehandelde vraag terug in het zicht.
124 const coll = queues.helpCollection('https://x/queues/help', 'oma');
125 assert.equal(coll['shaer:openComplete'], true);
126});
Note: See TracBrowser for help on using the repository browser.