Changeset 61d52ed in Klonkt for test/thread.test.js
- Timestamp:
- 10/14/2026 04:25:21 PM (6 hours ago)
- Branches:
- main
- Parents:
- 6e9bc70
- git-author:
- Bart <bart@…> (10/14/2026 04:23:29 PM)
- git-committer:
- Bart <bart@…> (10/14/2026 04:25:21 PM)
- File:
-
- 1 edited
-
test/thread.test.js (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
test/thread.test.js
r6e9bc70 r61d52ed 240 240 assert.equal(broken.body.error, 'source unreachable'); 241 241 }); 242 243 // ── Eén note, naar boven (de object-route) ─────────────────────────── 244 // 245 // De thread geeft alleen wat onder een post hangt. Een lezer die een antwoord 246 // kreeg en de ouder niet, vraagt de ouder hier. Wat hier stil kan breken: een 247 // ouder die verzonnen wordt (de thread-fallback die meelekt), een geblokkeerde 248 // die toch zichtbaar wordt, en een route zonder bearer-check. 249 test('getObject geeft de note zelf, met alleen de ouder die de bron noemde', async (t) => { 250 const vorige = globalThis.fetch; 251 t.after(() => { globalThis.fetch = vorige; }); 252 const KIND_REPLY = 'https://203.0.113.60/notes/antwoord'; 253 const WORTEL = 'https://203.0.113.60/notes/wortel'; 254 globalThis.fetch = async (url) => { 255 const u = String(url); 256 const json = (o) => new Response(JSON.stringify(o), { status: 200, headers: { 'content-type': 'application/activity+json' } }); 257 if (u === KIND_REPLY) return json({ id: KIND_REPLY, type: 'Note', attributedTo: TANTE, inReplyTo: WORTEL, content: '<p>ja <script>x</script></p>', published: '2026-09-07T13:42:26Z' }); 258 if (u === WORTEL) return json({ id: WORTEL, type: 'Note', attributedTo: TANTE, content: '<p>begin</p>' }); 259 if (u === TANTE) return json(actorDoc(TANTE, 'tante')); 260 return new Response('not found', { status: 404 }); 261 }; 262 263 const antwoord = await AP.getObject('kind', KIND_REPLY); 264 assert.equal(antwoord.found, true); 265 assert.equal(antwoord.note.inReplyTo, WORTEL, 'de ouder die de bron gaf'); 266 assert.equal(antwoord.note.attributedTo.name, 'tante'); 267 assert.ok(!antwoord.note.content.includes('<script'), 'geschoond'); 268 269 const wortel = await AP.getObject('kind', WORTEL); 270 assert.equal(wortel.found, true); 271 assert.equal(wortel.note.inReplyTo, undefined, 'geen ouder verzonnen voor een wortel'); 272 }); 273 274 test('getObject: een geblokkeerde auteur is er niet, een weigering is een weigering', async (t) => { 275 const vorige = globalThis.fetch; 276 t.after(() => { globalThis.fetch = vorige; }); 277 const VAN_NAAR = 'https://203.0.113.40/notes/van-naar'; 278 globalThis.fetch = async (url) => (String(url) === VAN_NAAR 279 ? new Response(JSON.stringify({ id: VAN_NAAR, type: 'Note', attributedTo: GEBLOKT, content: 'x' }), { status: 200 }) 280 : new Response('nope', { status: 410 })); 281 282 const geblokt = await AP.getObject('kind', VAN_NAAR); 283 assert.equal(geblokt.found, false); 284 assert.equal(geblokt.note, null); 285 286 const weg = await AP.getObject('kind', 'https://203.0.113.61/notes/weg'); 287 assert.equal(weg.found, false); 288 assert.equal(weg.sourceStatus, 410); 289 }); 290 291 test('de object-route: alleen met de eigen bearer, en een weigering is 404', async (t) => { 292 const crypto = await import('crypto'); 293 const express = (await import('express')).default; 294 const routes = (await import('../src/routes/activitypub.js')).default; 295 296 // Een eigen site zonder guardians, zodat de poorten van de tests hierboven 297 // (de ward met de dichte emoji-poort) hier niet meetellen. 298 db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run('s-lezer', 'lezer', 'Lezer', 'u1'); 299 const bearer = 'test-token-' + 'e'.repeat(24); 300 db.prepare('INSERT INTO oauth_tokens (token_hash, client_id, user_id, site_slug, scope) VALUES (?,?,?,?,?)') 301 .run(crypto.createHash('sha256').update(bearer).digest('base64url'), 'c', 'u1', 'lezer', 'read write'); 302 303 const app = express(); 304 app.use(routes); 305 const server = app.listen(0); 306 t.after(() => server.close()); 307 await new Promise((r) => server.once('listening', r)); 308 309 const vorige = globalThis.fetch; 310 t.after(() => { globalThis.fetch = vorige; }); 311 const OUDER = 'https://203.0.113.62/notes/ouder'; 312 globalThis.fetch = async (url) => { 313 const u = String(url); 314 if (u === OUDER) return new Response(JSON.stringify({ id: OUDER, type: 'Note', attributedTo: VREEMDE, content: '<p>de ouder</p>', inReplyTo: BRON }), { status: 200 }); 315 if (u === VREEMDE) return new Response(JSON.stringify(actorDoc(VREEMDE, 'vreemde')), { status: 200 }); 316 return new Response('nope', { status: 404 }); 317 }; 318 const ask = async (slug, uri, auth = `Bearer ${bearer}`) => { 319 const r = await realFetch( 320 `http://127.0.0.1:${server.address().port}/ap/users/${slug}/object?uri=${encodeURIComponent(uri)}`, 321 { headers: auth ? { Authorization: auth } : {} }); 322 return { status: r.status, body: r.status === 403 ? null : await r.json() }; 323 }; 324 325 assert.equal((await ask('lezer', OUDER, null)).status, 403, 'zonder bearer niets'); 326 assert.equal((await ask('kind', OUDER)).status, 403, 'niet met de bearer van een ander'); 327 assert.equal((await ask('lezer', 'http://203.0.113.62/notes/ouder')).status, 400, 'alleen https'); 328 329 const ok = await ask('lezer', OUDER); 330 assert.equal(ok.status, 200); 331 assert.equal(ok.body.id, OUDER); 332 assert.equal(ok.body.inReplyTo, BRON); 333 assert.equal(ok.body.attributedTo.name, 'vreemde'); 334 335 const dicht = await ask('lezer', 'https://203.0.113.63/notes/dicht'); 336 assert.equal(dicht.status, 404); 337 assert.equal(dicht.body.error, 'not shared by source'); 338 });
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)