// The app's composer posts over C2S. A top-level Note with media used to lose
// it silently: c2sCreatePost read only the content, so a photo post arrived
// naked while the very same attachments worked fine on replies and DMs.
import { test } from 'node:test';
import assert from 'node:assert/strict';
process.env.DATABASE_PATH = ':memory:';
process.env.PUBLIC_BASE_URL = 'https://test.example';
const dbMod = await import('../src/config/database.js');
const db = dbMod.default;
dbMod.initializeDatabase();
const AP = (await import('../src/services/ActivityPubService.js')).default;
db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
.run('u1', 'robin', 'u1@t', 'x', 'god');
db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_primary) VALUES (?,?,?,?,1)').run('s1', 'kid', 'kid', 'u1');
const site = db.prepare('SELECT * FROM sites WHERE slug = ?').get('s1' ? 'kid' : 'kid');
const user = db.prepare('SELECT * FROM users WHERE id = ?').get('u1');
test('a C2S post carries its media: into the web content and out as AS2 attachments', async () => {
const r = await AP.ingestOutboxActivity(site, user, {
type: 'Create',
object: {
type: 'Note',
content: '
kijk dan
',
source: { content: 'kijk dan', mediaType: 'text/plain' },
to: ['https://test.example/ap/users/kid/followers'],
cc: ['https://www.w3.org/ns/activitystreams#Public'],
attachment: [
{ type: 'Image', url: '/media/reply-media/foto.jpg', mediaType: 'image/jpeg', name: 'ons plein' },
{ type: 'Audio', url: '/media/reply-media/opname.m4a', mediaType: 'audio/mp4' },
// Not ours: a remote URL must never be laundered into our media.
{ type: 'Image', url: 'https://evil.test/x.jpg', mediaType: 'image/jpeg' },
],
},
});
assert.equal(r.status, 201, 'the post is created');
const post = db.prepare('SELECT * FROM posts WHERE id = ?').get(r.id);
assert.match(post.content, //, 'the web shows the photo');
assert.match(post.content, /