| [8151340] | 1 | // Gemengde tijdstempels sorteren chronologisch (shaer-a937).
|
|---|
| 2 | //
|
|---|
| 3 | // Deze database draagt twee spellingen: '2026-08-06 09:02:01' van
|
|---|
| 4 | // CURRENT_TIMESTAMP en '2026-08-06T02:08:01.000Z' van toISOString(). SQLite
|
|---|
| 5 | // vergelijkt TEKST, en op positie 10 staat een 'T' (0x54) tegenover een spatie
|
|---|
| 6 | // (0x20) -- dus binnen dezelfde dag wint de ISO-vorm altijd, hoe laat hij ook
|
|---|
| 7 | // is. Gemeten op de live database van sound-fabrics: een antwoord van 02:08
|
|---|
| 8 | // stond boven likes van 09:02 diezelfde dag, zichtbaar onder elke post met
|
|---|
| 9 | // reacties EN likes.
|
|---|
| 10 | //
|
|---|
| 11 | // De toets zet de twee vormen bewust door elkaar met de ISO-rij VROEGER op de
|
|---|
| 12 | // dag: precies de stand waarin een ongenormaliseerde sortering omvalt.
|
|---|
| 13 | import { test } from 'node:test';
|
|---|
| 14 | import assert from 'node:assert/strict';
|
|---|
| 15 |
|
|---|
| 16 | process.env.DATABASE_PATH = ':memory:';
|
|---|
| 17 | process.env.PUBLIC_BASE_URL = 'https://test.example';
|
|---|
| 18 |
|
|---|
| 19 | const dbMod = await import('../src/config/database.js');
|
|---|
| 20 | const db = dbMod.default;
|
|---|
| 21 | { const stil = console.log; console.log = () => {}; try { dbMod.initializeDatabase(); } finally { console.log = stil; } }
|
|---|
| 22 | const AP = (await import('../src/services/ActivityPubService.js')).default;
|
|---|
| 23 |
|
|---|
| 24 | db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
|
|---|
| 25 | .run('u1', 'robin', 'u1@t', 'x', 'god');
|
|---|
| 26 | db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_primary) VALUES (?,?,?,?,1)').run('s1', 'kid', 'kid', 'u1');
|
|---|
| 27 | db.prepare(`INSERT INTO posts (id, site_id, author_id, slug, title, content, status, published_at)
|
|---|
| 28 | VALUES ('p1','s1','u1','post','Post','<p>x</p>','published','2026-08-01T00:00:00Z')`).run();
|
|---|
| 29 |
|
|---|
| 30 | // SQL-notatie om 09:02, ISO om 02:08 -- dezelfde dag. Chronologisch hoort de
|
|---|
| 31 | // SQL-rij bovenaan; op tekst gesorteerd wint de ISO-rij.
|
|---|
| 32 | const LAAT_SQL = '2026-08-06 09:02:01';
|
|---|
| 33 | const VROEG_ISO = '2026-08-06T02:08:01.000Z';
|
|---|
| 34 |
|
|---|
| 35 | const insI = db.prepare(`INSERT INTO ap_interactions (kind, post_id, object_uri, actor_uri, actor_name, content, published, created_at)
|
|---|
| 36 | VALUES (?,?,?,?,?,?,?,?)`);
|
|---|
| 37 | insI.run('reply', 'p1', 'https://elders.example/n/vroeg', 'https://elders.example/u/a', 'A', '<p>vroeg</p>', VROEG_ISO, VROEG_ISO);
|
|---|
| 38 | insI.run('reply', 'p1', 'https://elders.example/n/laat', 'https://elders.example/u/b', 'B', '<p>laat</p>', null, LAAT_SQL);
|
|---|
| 39 |
|
|---|
| 40 | const insT = db.prepare(`INSERT INTO ap_timeline (id, slug, author_uri, author_name, content, published, created_at)
|
|---|
| 41 | VALUES (?,?,?,?,?,?,?)`);
|
|---|
| 42 | insT.run('https://elders.example/t/vroeg', 'kid', 'https://elders.example/u/a', 'A', '<p>vroeg</p>', VROEG_ISO, VROEG_ISO);
|
|---|
| 43 | insT.run('https://elders.example/t/laat', 'kid', 'https://elders.example/u/b', 'B', '<p>laat</p>', null, LAAT_SQL);
|
|---|
| 44 |
|
|---|
| 45 | const insM = db.prepare(`INSERT INTO ap_mentions (slug, object_uri, actor_uri, actor_name, content, published, created_at)
|
|---|
| 46 | VALUES (?,?,?,?,?,?,?)`);
|
|---|
| 47 | insM.run('kid', 'https://elders.example/m/vroeg', 'https://elders.example/u/a', 'A', '<p>vroeg</p>', VROEG_ISO, VROEG_ISO);
|
|---|
| 48 | insM.run('kid', 'https://elders.example/m/laat', 'https://elders.example/u/b', 'B', '<p>laat</p>', null, LAAT_SQL);
|
|---|
| 49 |
|
|---|
| 50 | /** Het nieuwste eerst: hoort de LAAT-rij (09:02) te zijn, niet de ISO van 02:08. */
|
|---|
| 51 | const nieuwsteIs = (rijen, veld, verwacht) => {
|
|---|
| 52 | assert.ok(rijen.length >= 2, 'beide rijen horen terug te komen');
|
|---|
| 53 | assert.match(String(rijen[0][veld]), verwacht,
|
|---|
| 54 | `de rij van 09:02 hoort bovenaan; kreeg ${rijen[0][veld]} -- dan sorteert de tekst en niet de tijd`);
|
|---|
| 55 | };
|
|---|
| 56 |
|
|---|
| 57 | test('de tijdlijn zet 09:02 boven 02:08, ook in twee spellingen', () => {
|
|---|
| 58 | nieuwsteIs(AP.getTimeline('kid', 10, 0), 'id', /laat/);
|
|---|
| 59 | });
|
|---|
| 60 |
|
|---|
| 61 | test('de reacties onder een post ook -- de fout die gemeten werd', () => {
|
|---|
| 62 | nieuwsteIs(AP.getReplyMessages('kid', 10), 'object_uri', /laat/);
|
|---|
| 63 | });
|
|---|
| 64 |
|
|---|
| 65 | test('en de berichten', () => {
|
|---|
| 66 | nieuwsteIs(AP.getDirectMessages('kid', 10), 'object_uri', /laat/);
|
|---|
| 67 | });
|
|---|
| 68 |
|
|---|
| 69 | test('een onleesbare stempel valt niet weg, en bederft de rest niet', () => {
|
|---|
| 70 | // strftime geeft NULL op iets dat het niet als tijd herkent; de COALESCE valt
|
|---|
| 71 | // dan terug op de rauwe waarde. WAAR die rij landt is onbepaald -- als tekst
|
|---|
| 72 | // staat 'geen datum' nu eenmaal boven '2026-...' -- en dat is de goede ruil:
|
|---|
| 73 | // data die je niet begrijpt bewaar je. Wat hier telt is dat de rij blijft
|
|---|
| 74 | // bestaan en dat de twee ECHTE spellingen onderling nog kloppen.
|
|---|
| 75 | insT.run('https://elders.example/t/kapot', 'kid', 'https://elders.example/u/c', 'C', '<p>kapot</p>', 'geen datum', 'geen datum');
|
|---|
| 76 | const rijen = AP.getTimeline('kid', 10, 0);
|
|---|
| 77 | assert.equal(rijen.length, 3, 'de rij met de onleesbare stempel valt niet weg');
|
|---|
| 78 | const ids = rijen.map((r) => r.id).filter((id) => !/kapot/.test(id));
|
|---|
| 79 | assert.match(ids[0], /laat/, 'onder de leesbare rijen staat 09:02 nog steeds boven 02:08');
|
|---|
| 80 | });
|
|---|