Index: test/guardian-longpoll.test.js
===================================================================
--- test/guardian-longpoll.test.js	(revision 6fab3f3e28add90b1140273d9a28793236cd7a39)
+++ test/guardian-longpoll.test.js	(revision 6fab3f3e28add90b1140273d9a28793236cd7a39)
@@ -0,0 +1,97 @@
+// De wekker onder het Guardian-paneel (Barts opdracht, 9-8).
+//
+// Het paneel tikte elke 45 seconden, ongeacht of er iets gebeurd was. Nu hangt
+// er een verzoek open tot er iets is dat de guardian moet verwerken.
+//
+// Wat hier bewaakt wordt is de BUS, niet de route: dat elke soort gebeurtenis
+// wekt, dat een wachter maar EEN keer gewekt wordt, en dat een wachter die zich
+// afmeldt ook echt weg is. Een lange poll die niet wekt is niet te onderscheiden
+// van een trage server -- en dat is precies het soort fout dat niemand meldt.
+//
+// Run: npm test
+import { test } from 'node:test';
+import assert from 'node:assert/strict';
+
+process.env.DATABASE_PATH = ':memory:';
+const AP = await import('../src/services/ActivityPubService.js');
+
+test('een wachter wordt gewekt', () => {
+  let gewekt = 0;
+  AP.onGuardian('kind', () => { gewekt += 1; });
+  AP.wakeGuardian('kind');
+  assert.equal(gewekt, 1);
+});
+
+test('en daarna vergeten: een tweede wek doet niets', () => {
+  // Het antwoord dat volgt is de nieuwe waarheid; de client komt terug met een
+  // nieuwe wachter. Bleef hij staan, dan zou een burst van tien gebeurtenissen
+  // tien antwoorden op hetzelfde verzoek proberen te schrijven.
+  let gewekt = 0;
+  AP.onGuardian('kind2', () => { gewekt += 1; });
+  AP.wakeGuardian('kind2');
+  AP.wakeGuardian('kind2');
+  assert.equal(gewekt, 1);
+});
+
+test('afmelden werkt', () => {
+  // Hangt de client op, dan moet de wachter weg. Anders lekt elke afgebroken
+  // verbinding een callback, en bij een lange poll is dat elke 25 seconden een.
+  let gewekt = 0;
+  const af = AP.onGuardian('kind3', () => { gewekt += 1; });
+  af();
+  AP.wakeGuardian('kind3');
+  assert.equal(gewekt, 0);
+});
+
+test('wekken raakt alleen de eigen guardian', () => {
+  let a = 0; let b = 0;
+  AP.onGuardian('oma', () => { a += 1; });
+  AP.onGuardian('opa', () => { b += 1; });
+  AP.wakeGuardian('oma');
+  assert.equal(a, 1);
+  assert.equal(b, 0);
+});
+
+test('meerdere schermen van dezelfde guardian worden allemaal gewekt', () => {
+  // Telefoon en laptop naast elkaar. Zou alleen de laatste wachter gewekt
+  // worden, dan blijft het andere scherm stil staan.
+  let n = 0;
+  AP.onGuardian('duo', () => { n += 1; });
+  AP.onGuardian('duo', () => { n += 1; });
+  AP.wakeGuardian('duo');
+  assert.equal(n, 2);
+});
+
+test('een wachter die stukgaat breekt de rest niet', () => {
+  // Een kapotte callback mag nooit de andere schermen laten hangen.
+  let goed = 0;
+  AP.onGuardian('stuk', () => { throw new Error('boem'); });
+  AP.onGuardian('stuk', () => { goed += 1; });
+  AP.wakeGuardian('stuk');
+  assert.equal(goed, 1);
+});
+
+test('wekken zonder wachters is geen fout', () => {
+  assert.doesNotThrow(() => AP.wakeGuardian('niemand'));
+});
+
+test('ELKE guardianship-gebeurtenis wekt, ook die geen melding waard is', () => {
+  // De kern van Barts opdracht: push kiest bewust een handvol soorten, het
+  // scherm hoort ze allemaal te weten. Dit gaat door de ECHTE handler heen --
+  // een eerdere versie toetste alleen de bus, en toen bleef de mutatie die het
+  // wekken uit onEvent haalde gewoon groen.
+  const ev = { kind: 'lapse_vote', lapse: 'x', by: 'y', state: 'recorded' };
+  assert.equal(AP.guardianEventPush('kind9', ev), null, 'deze soort geeft geen melding');
+
+  let gewekt = 0;
+  AP.onGuardian('kind9', () => { gewekt += 1; });
+  AP.onGuardianshipEvent('kind9', ev);
+  assert.equal(gewekt, 1, 'en toch wordt het paneel gewekt');
+});
+
+test('een gebeurtenis die WEL een melding is, wekt ook', () => {
+  let gewekt = 0;
+  AP.onGuardian('kind10', () => { gewekt += 1; });
+  AP.onGuardianshipEvent('kind10', { kind: 'committed', ward: 'https://w/x' });
+  assert.equal(gewekt, 1);
+});
