source: Klonkt/test/unread.test.js@ e215090

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

Ongelezen per gesprek: een COUNT, en Read als gebeurtenis

shaer-frontend-3tx. De kopjeslezing draagt nu shaer:unread per gesprek, plus
shaer:unreadWave als er een zwaai bij zit -- een zetje van een guardian is geen
gesprek en hoort niet opgeteld te worden.

GEEN BIJGEHOUDEN GETAL (Barts besluit): het aantal is een COUNT over de
berichten na de leesmarkering. Niets om op te hogen bij bezorging, niets om te
verlagen bij lezen, en bij een verwijdering klopt het vanzelf weer -- een
teller zou blijven staan en dan zegt de badge 3 terwijl er niets is. Er staat
een toets op precies dat geval.

READ IS EEN GEBEURTENIS, geen zetbare stand (Barts vraag: een stille client
stuurt toch geen Undo{Read}?). AS2 kent Read; markRead neemt het maximum, dus
een toestel dat een week uit stond kan gelezen berichten niet terugzetten op
ongelezen. Ook daar staat een toets op. Geen shaer:seen verzonnen.

De markering is de samengestelde cursor (stempel|ref), zelfde vorm als de
gesprekspaginering: twee berichten in dezelfde seconde is bij DM's een gesprek
en geen randgeval.

En conversationItems geeft nu PAREN terug in plaats van een losse lijst. Een
kop levert niet altijd een item op (dichte poort, ontbrekende rij), en op index
koppelen zou de telling stil aan het verkeerde gesprek hangen.

862/862 groen.

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

  • Property mode set to 100644
File size: 3.2 KB
Line 
1// De badge: ongelezen per gesprek (shaer-frontend-3tx).
2//
3// Twee dingen liggen hier vast. Een COUNT en geen bijgehouden getal -- bij een
4// verwijdering klopt het dan vanzelf weer, waar een teller zou blijven staan.
5// En Read als GEBEURTENIS: markeren is optellend, dus een toestel dat een week
6// uit stond kan je gelezen berichten niet terugzetten op ongelezen.
7//
8// In-memory SQLite. Run: npm test
9import { test } from 'node:test';
10import assert from 'node:assert/strict';
11
12process.env.DATABASE_PATH = ':memory:';
13process.env.PUBLIC_BASE_URL = 'https://klonkt.test';
14
15const dbMod = await import('../src/config/database.js');
16const db = dbMod.default;
17dbMod.initializeDatabase();
18const AP = await import('../src/services/ActivityPubService.js');
19
20db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
21 .run('u1', 'u1', 'u1@t', 'x', 'god');
22db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run('s1', 'kind', 'Kind', 'u1');
23
24const OMA = 'https://elders/u/oma';
25const OOM = 'https://elders/u/oom';
26const arrived = (uri, from, stamp, wave = 0) => db.prepare(
27 `INSERT INTO ap_mentions (slug, object_uri, actor_uri, actor_name, content, published, wave)
28 VALUES ('kind', ?, ?, 'iemand', '<p>hoi</p>', ?, ?)`).run(uri, from, stamp, wave);
29
30arrived('https://elders/n/1', OMA, '2026-08-01T10:00:00Z');
31arrived('https://elders/n/2', OMA, '2026-08-01T11:00:00Z');
32arrived('https://elders/n/3', OMA, '2026-08-01T12:00:00Z');
33arrived('https://elders/n/w', OOM, '2026-08-01T09:00:00Z', 1);
34
35test('alles is ongelezen tot je iets gelezen hebt', () => {
36 const u = AP.unreadPerConversation('kind');
37 assert.equal(u.get(OMA).n, 3);
38 assert.equal(u.get(OOM).n, 1);
39 // Een zwaai telt apart: dat is een zetje van een guardian, geen gesprek.
40 assert.equal(u.get(OOM).wave, true);
41 assert.equal(u.get(OMA).wave, false);
42});
43
44test('lezen tot het tweede bericht laat er een over', () => {
45 AP.markRead('kind', 'https://elders/n/2');
46 assert.equal(AP.unreadPerConversation('kind').get(OMA).n, 1);
47 assert.equal(AP.unreadPerConversation('kind').get(OOM).n, 1, 'een ander gesprek raakt het niet');
48});
49
50test('een oud toestel kan gelezen berichten niet terugzetten', () => {
51 // Precies het geval waar Read-als-gebeurtenis voor is: markeren is
52 // optellend, dus dit doet niets.
53 AP.markRead('kind', 'https://elders/n/1');
54 assert.equal(AP.unreadPerConversation('kind').get(OMA).n, 1, 'nog steeds een');
55});
56
57test('alles gelezen is geen rij, niet een nul', () => {
58 AP.markRead('kind', 'https://elders/n/3');
59 assert.equal(AP.unreadPerConversation('kind').get(OMA), undefined);
60});
61
62test('een verwijdering laat het getal vanzelf kloppen', () => {
63 // Waar een bijgehouden teller zou blijven staan: badge zegt 1, er is niets.
64 arrived('https://elders/n/4', OMA, '2026-08-01T13:00:00Z');
65 assert.equal(AP.unreadPerConversation('kind').get(OMA).n, 1);
66 db.prepare("DELETE FROM ap_mentions WHERE object_uri = ?").run('https://elders/n/4');
67 assert.equal(AP.unreadPerConversation('kind').get(OMA), undefined);
68});
69
70test('een onbekende note markeert niets, en dat is geen fout', () => {
71 assert.equal(AP.markRead('kind', 'https://elders/n/bestaat-niet'), null);
72});
Note: See TracBrowser for help on using the repository browser.