source: Klonkt/test/cirkel-media-vorm.test.js

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

/cirkel viel om op een media_json die geen array was

boiert.eu gaf 500 op /cirkel terwijl de voorpagina het deed. De catch in
safeJson ving KAPOTTE json af, maar niet geldige json van het verkeerde TYPE:
een remote server stuurde media_json = "[]" -- een STRING met daarin [] -- en
JSON.parse geeft dan netjes een string terug. Een string heeft geen .map:

TypeError: safeJson(...).map is not a function

at coverMedia (src/routes/circle.js:25)

Een enkele post nam zo de hele pagina mee, en beide paden (met en zonder
?append=1) omdat ze dezelfde mapping delen.

DEZE FOUT WAS AL EENS GEVONDEN EN GEREPAREERD, in views/partials/note-body.ejs,
met de reden erbij: "drie posts van een remote server droegen media_json = '[]'
[...] en daarmee lag de hele Krant plat: een vreemde note nam de pagina mee. Wat
er binnenkomt is niet van ons, dus de vorm moet hier afgedwongen worden en niet
aangenomen." Diezelfde les was in circle.js nooit toegepast -- zelfde data,
andere route.

De drie andere plekken die media_json parsen (ActivityPubService 496, 881, 4659)
dwingen de vorm wel af; die zijn nagelopen en blijven zoals ze zijn.

GEVONDEN ZONDER TOEGANG tot die machine. /cirkel?offset=999999 gaf 200, dus een
lege pagina rendert prima en de fout zat in een RIJ. Daarna van buitenaf
uitgebinaird over ?offset=: rij 19 van de eerste 72. Robin leverde daarna de
stacktrace, die precies dat bevestigde.

Vier tests, met de vorm die het omver haalde als eerste. Gecontroleerd dat ze
bijten: reparatie eruit -> 0 groen, 4 rood; terug -> 4 groen.

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

  • Property mode set to 100644
File size: 3.3 KB
Line 
1// /cirkel viel om op een media_json die geldig JSON was maar geen array.
2//
3// Gevonden op 14 augustus 2026: boiert.eu gaf 500 op /cirkel terwijl de
4// voorpagina het deed. Van buitenaf uitgebinaird over ?offset= -- rij 19 van de
5// eerste 72 brak het -- en de stacktrace bevestigde het:
6//
7// TypeError: safeJson(...).map is not a function
8// at coverMedia (src/routes/circle.js:25)
9//
10// Een remote server stuurde media_json = "[]": een STRING met daarin `[]`.
11// JSON.parse geeft dan een string terug, en een string heeft geen .map. De
12// catch ving alleen KAPOTTE json, niet geldige json van het verkeerde type.
13//
14// Exact dezelfde fout stond al beschreven in views/partials/note-body.ejs, waar
15// drie vreemde notes de Krant meenamen. Die les was hier nooit toegepast.
16import { test } from 'node:test';
17import assert from 'node:assert/strict';
18
19process.env.DATABASE_PATH = ':memory:';
20process.env.PUBLIC_BASE_URL = 'https://ons.test';
21
22const dbMod = await import('../src/config/database.js');
23const db = dbMod.default;
24dbMod.initializeDatabase();
25const express = (await import('express')).default;
26const circle = (await import('../src/routes/circle.js')).default;
27
28db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
29 .run('u1', 'u1', 'u1@t', 'x', 'god');
30db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_public) VALUES (?,?,?,?,1)')
31 .run('s1', 'dev', 'Dev', 'u1');
32
33// Een post in de cirkel met precies de vorm die het omver haalde.
34const zet = (id, media) => {
35 db.prepare(`INSERT INTO ap_timeline (id, slug, author_uri, author_name, content, url, published, media_json)
36 VALUES (?,?,?,?,?,?,?,?)`)
37 .run(id, 'dev', 'https://elders.test/u/a', 'Iemand', '<p><strong>Titel</strong></p>',
38 'https://elders.test/n/' + id, '2026-08-13T12:00:00Z', media);
39 db.prepare(`INSERT INTO ap_my_reactions (site_slug, target_uri, kind) VALUES (?,?,'boost')`)
40 .run('dev', id);
41};
42
43const app = express();
44app.use((req, res, next) => {
45 res.locals.site = db.prepare("SELECT * FROM sites WHERE id = 's1'").get();
46 res.locals.siteUrlBase = '';
47 next();
48});
49app.use(circle);
50app.use((req, res) => res.status(404).end());
51const server = app.listen(0);
52await new Promise((r) => server.once('listening', r));
53const poort = server.address().port;
54const haal = async (pad) => (await fetch(`http://127.0.0.1:${poort}${pad}`)).status;
55
56test('media_json als STRING met [] erin sloopte de pagina', async () => {
57 zet('n-string', '"[]"');
58 assert.equal(await haal('/cirkel'), 200, 'geen 500 meer');
59 assert.equal(await haal('/cirkel?append=1'), 200, 'ook niet op het append-pad');
60});
61
62test('en de andere vormen die geen array zijn ook niet', async () => {
63 // Geldig JSON, verkeerd type: een object, een getal, een kale string, null.
64 zet('n-obj', '{"url":"https://x/y.jpg"}');
65 zet('n-num', '42');
66 zet('n-str', '"gewoon tekst"');
67 zet('n-null', 'null');
68 assert.equal(await haal('/cirkel'), 200);
69});
70
71test('een ECHTE array werkt nog gewoon', async () => {
72 zet('n-goed', JSON.stringify([{ url: 'https://elders.test/p.jpg', type: 'image/jpeg' }]));
73 assert.equal(await haal('/cirkel'), 200);
74});
75
76test('en kapotte json blijft ook afgevangen', async () => {
77 zet('n-kapot', '{niet eens json');
78 assert.equal(await haal('/cirkel'), 200);
79});
80
81test.after(() => server.close());
Note: See TracBrowser for help on using the repository browser.