| 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.
|
|---|
| 12 | import { test, before } from 'node:test';
|
|---|
| 13 | import assert from 'node:assert/strict';
|
|---|
| 14 |
|
|---|
| 15 | process.env.DATABASE_PATH = ':memory:';
|
|---|
| 16 |
|
|---|
| 17 | const mod = await import('../src/config/database.js');
|
|---|
| 18 | const db = mod.default;
|
|---|
| 19 |
|
|---|
| 20 | before(() => {
|
|---|
| 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 |
|
|---|
| 33 | const rev = () => db.prepare('SELECT n FROM ap_feed_rev').get().n;
|
|---|
| 34 | const sleutels = () => db.prepare('SELECT object_uri FROM ap_feed_state').all().map((r) => r.object_uri);
|
|---|
| 35 | const 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 |
|
|---|
| 39 | test('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 |
|
|---|
| 45 | test('een boost wekt geen wachters', () => {
|
|---|
| 46 | const voor = rev();
|
|---|
| 47 | voegToe('announce', '');
|
|---|
| 48 | assert.equal(rev(), voor);
|
|---|
| 49 | });
|
|---|
| 50 |
|
|---|
| 51 | test('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 |
|
|---|
| 55 | test('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 |
|
|---|
| 62 | test('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 |
|
|---|
| 68 | test('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 | });
|
|---|