source: Klonkt/test/feed-state-triggers.test.js@ ba76bf5

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

Drie gaten uit de 1.7.0-review: index, wekkers, en de exporter-tijdzone

Drie losstaande reparaties uit de review van de 66 commits. Elk mechanisch, elk met
een tegenproef dat de test zonder de fix ook echt faalt.

  1. De permalink-lookup van reacties had geen index. canonicalReactionUri zoekt op

(slug, url) en viel terug op idx_ap_timeline_slug, dus een scan van elke rij van die
slug. Dat gebeurt PER REACTIE in getInteractions, en de reactie-migratie erft het in
haar re-key-join die synchroon voor listen draait: de opstartkosten waren reacties
maal tijdlijnrijen. Nagemeten met EXPLAIN QUERY PLAN, nu een indexzoek.

  1. De feed-wekkers gingen af op likes en boosts. ap_interactions draagt naast replies

ook likes en announces, en die schrijven een LEGE object_uri (regel 2310, tegenover
o.id bij replies). Zonder WHEN bumpte elke inkomende like de rev, werd elke
long-poll-wachter gewekt, en kreeg die de hele collectie opnieuw terwijl er niets aan
veranderd was: precies de kosten die de 304 moest wegnemen. Bovendien belandde er een
rij op de lege string in ap_feed_state, die feedChangesSince als sleutel uitdeelt. Bij
ap_timeline was hier wel op gelet (de UPDATE OF sluit liked en boosted uit) en een
tabel verder vergeten.

  1. De exporter schreef lokale tijd als UTC. Kale Date.parse op SQLite's

'YYYY-MM-DD HH:MM:SS' leest dat als lokale tijd, dus op UTC+2 ging er twee uur van
elke stempel af voordat hij het archief in ging. Die verschuiving wordt bij het
exporteren INGEBAKKEN en valt niet weg bij het importeren. Het raakte de stempels die
de database zelf zet (concepten, ingeplande posts, gearchiveerde antwoorden), niet die
uit de editor, dus de schade was stil en gedeeltelijk. Dezelfde correctie stond al
twee keer in deze codebase, in isoStamp() en stampMs(); de exporter had de les gemist.
EXPORT-FORMAT.md schreef altijd al "ISO 8601, UTC" voor.

Changed files:
src/config/database.js

  • index idx_ap_timeline_url op (slug, url)
  • zet() kreeg een optionele wanneer-parameter voor een WHEN op de trigger
  • de drie ap_interactions-triggers filteren nu op kind='reply' (OLD bij delete)

src/services/ArchiveExportService.js

  • toISO leest SQL-notatie expliciet als UTC

New file:
test/feed-state-triggers.test.js

  • like en boost wekken niets, reply wel, verwijderde reply ook
  • er staat nooit een rij op de lege sleutel
  • en de permalink-lookup gebruikt de index, via EXPLAIN QUERY PLAN

remarks: tegenproef gedaan op alle drie. Zonder de database-fixes 4 van 6 rood; zonder
de exporter-fix valt de archief-test om in Europe/Amsterdam en niet in UTC. Volledige
suite 587 groen in beide tijdzones. De vierde vondst, de guardian-controle op
hulpmarkeringen, zit hier NIET in: die vraagt de guardian-set van de ward federatief
op te halen via shaer:guardians en dat is ontwerpwerk, geen mechanische fix.

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

  • Property mode set to 100644
File size: 3.4 KB
Line 
1// De feed-wekkers mogen alleen op echte inhoud afgaan.
2//
3// ap_interactions draagt naast replies ook likes en announces, en die schrijven een
4// LEGE object_uri. Zonder een WHEN op kind bumpte elke inkomende like de rev, werd
5// elke long-poll-wachter gewekt, en kreeg die de hele collectie opnieuw terwijl er
6// niets aan veranderd was: precies de kosten die de 304 moest wegnemen. Bovendien
7// belandde er een rij op de lege string in ap_feed_state, die feedChangesSince
8// vervolgens als sleutel uitdeelt.
9//
10// Bij ap_timeline is hier wel op gelet (de UPDATE OF sluit liked en boosted uit).
11// Deze test legt vast dat het bij ap_interactions ook zo blijft.
12import { test, before } from 'node:test';
13import assert from 'node:assert/strict';
14
15process.env.DATABASE_PATH = ':memory:';
16
17const mod = await import('../src/config/database.js');
18const db = mod.default;
19
20before(() => {
21 const stil = console.log;
22 console.log = () => {}; // het schema-initialisatielogboek is hier ruis
23 try { mod.initializeDatabase(); } finally { console.log = stil; }
24
25 db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
26 .run('u1', 'u1', 'u1@test', 'x', 'god');
27 db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)')
28 .run('s1', 'me', 'Mijn site', 'u1');
29 db.prepare('INSERT INTO posts (id, site_id, slug, author_id, title, content, status) VALUES (?,?,?,?,?,?,?)')
30 .run('p1', 's1', 't', 'u1', 'T', 'C', 'published');
31});
32
33const rev = () => db.prepare('SELECT n FROM ap_feed_rev').get().n;
34const sleutels = () => db.prepare('SELECT object_uri FROM ap_feed_state').all().map((r) => r.object_uri);
35const voegToe = (kind, uri) => db
36 .prepare('INSERT INTO ap_interactions (kind, post_id, object_uri, actor_uri) VALUES (?,?,?,?)')
37 .run(kind, 'p1', uri, `https://elders.example/${kind}`);
38
39test('een like wekt geen wachters', () => {
40 const voor = rev();
41 voegToe('like', '');
42 assert.equal(rev(), voor, 'een like verandert de collectie niet, dus hoeft niemand te worden gewekt');
43});
44
45test('een boost wekt geen wachters', () => {
46 const voor = rev();
47 voegToe('announce', '');
48 assert.equal(rev(), voor);
49});
50
51test('er staat nooit een rij op de lege sleutel', () => {
52 assert.ok(!sleutels().includes(''), 'een lege object_uri als sleutel wordt door feedChangesSince uitgedeeld');
53});
54
55test('een reply wekt de wachters wel', () => {
56 const voor = rev();
57 voegToe('reply', 'https://elders.example/note/9');
58 assert.equal(rev(), voor + 1);
59 assert.ok(sleutels().includes('https://elders.example/note/9'));
60});
61
62test('een verdwenen reply wekt de wachters ook', () => {
63 const voor = rev();
64 db.prepare("DELETE FROM ap_interactions WHERE kind = 'reply'").run();
65 assert.equal(rev(), voor + 1, 'anders blijft een verwijderd antwoord bij de client staan');
66});
67
68test('de permalink-lookup van reacties gebruikt een index', () => {
69 // canonicalReactionUri zoekt op (slug, url). Viel dat terug op idx_ap_timeline_slug,
70 // dan was het een scan van elke rij van die slug, per reactie, en de reactie-migratie
71 // erfde dat in haar re-key-join die synchroon voor listen draait.
72 const plan = db.prepare('EXPLAIN QUERY PLAN SELECT id FROM ap_timeline WHERE slug=? AND url=?')
73 .all('x', 'y').map((r) => r.detail).join(' ');
74 assert.match(plan, /USING INDEX idx_ap_timeline_url/, `verwachtte de url-index, kreeg: ${plan}`);
75 assert.doesNotMatch(plan, /SCAN ap_timeline/);
76});
Note: See TracBrowser for help on using the repository browser.