source: Klonkt/test/c2s-inbox.test.js@ 38d5e40

main
Last change on this file since 38d5e40 was fb30b5d, checked in by Robin Genis <roboburr@…>, 7 weeks ago

C2S inbox carries friends' media as AS2 attachments

Shaer showed images on own posts (outbox serialises attachment) but not on
friends' posts: the C2S inbox GET built its Create(Note) items without the
stored media. The timeline rows already keep media_json ([{url, type}],
written on the inbound Create); it just never reached the wire. A new pure
helper maps it to an AS2 attachment array so clients render friends' images
and audio exactly like own outbox posts.

Changed files:
src/services/ActivityPubService.js

  • timelineAttachments(mediaJson): media_json -> AS2 attachment array (Document + mediaType + url); empty or malformed JSON yields undefined and never blocks the item
  • exported alongside getTimeline

src/routes/activitypub.js

  • inbox GET: object.attachment = AP.timelineAttachments(t.media_json)

test/c2s-inbox.test.js

  • covers the mapping, the unknown-mediaType case, dropped rows without url, and malformed media_json (148 tests total, all green)

Alt text is not in media_json yet, so attachment.name stays empty for now.

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

  • Property mode set to 100644
File size: 2.5 KB
Line 
1// C2S inbox read (owner only): the route maps ap_timeline rows to Create(Note)
2// items; here we cover the data path (fields the mapping relies on). The
3// bearer gate itself follows the followers-route pattern (verified live).
4import { test } from 'node:test';
5import assert from 'node:assert/strict';
6
7process.env.DATABASE_PATH = ':memory:';
8process.env.PUBLIC_BASE_URL = 'https://klonkt.test';
9
10const dbMod = await import('../src/config/database.js');
11const db = dbMod.default;
12dbMod.initializeDatabase();
13const AP = (await import('../src/services/ActivityPubService.js')).default;
14
15db.prepare(`INSERT INTO ap_timeline (id, slug, author_uri, author_name, content, url, published, nsfw, cw, created_at)
16 VALUES ('https://r.test/n/1','me','https://r.test/u/a','A','<p>hoi</p>','https://r.test/@a/1','2026-07-01T10:00:00Z',1,'let op',CURRENT_TIMESTAMP)`).run();
17
18test('timeline rows carry the fields the inbox mapping needs', () => {
19 const [t] = AP.getTimeline('me', 10);
20 assert.equal(t.id, 'https://r.test/n/1');
21 assert.equal(t.author_uri, 'https://r.test/u/a');
22 assert.equal(t.content, '<p>hoi</p>');
23 assert.equal(t.url, 'https://r.test/@a/1');
24 assert.equal(t.nsfw, 1);
25 assert.equal(t.cw, 'let op');
26 assert.ok(t.published);
27});
28
29// Friends' media must reach the client: media_json ([{url, type}]) becomes the
30// AS2 attachment array on the inbox item, like own outbox posts (Shaer P2).
31test('media_json maps to AS2 attachments', () => {
32 const rows = AP.timelineAttachments(JSON.stringify([
33 { url: 'https://r.test/m/p.png', type: 'image/png' },
34 { url: 'https://r.test/m/a.mp3', type: 'audio/mpeg' },
35 ]));
36 assert.equal(rows.length, 2);
37 assert.deepEqual(rows[0], { type: 'Document', mediaType: 'image/png', url: 'https://r.test/m/p.png' });
38 assert.deepEqual(rows[1], { type: 'Document', mediaType: 'audio/mpeg', url: 'https://r.test/m/a.mp3' });
39});
40
41test('unknown mediaType stays undefined, bad rows drop', () => {
42 const rows = AP.timelineAttachments(JSON.stringify([
43 { url: 'https://r.test/m/x.bin', type: '' },
44 { type: 'image/png' }, // no url -> dropped
45 ]));
46 assert.equal(rows.length, 1);
47 assert.equal(rows[0].mediaType, undefined);
48});
49
50test('empty or malformed media_json yields undefined and never throws', () => {
51 assert.equal(AP.timelineAttachments(null), undefined);
52 assert.equal(AP.timelineAttachments('[]'), undefined);
53 assert.equal(AP.timelineAttachments('not json'), undefined);
54 assert.equal(AP.timelineAttachments('{"not":"a list"}'), undefined);
55});
Note: See TracBrowser for help on using the repository browser.