| 1 | // Paid posts slice 2 (klonkt-demo-aki): a paid post federates a PUBLIC teaser +
|
|---|
| 2 | // link, never its full content, so nothing leaks past the paywall.
|
|---|
| 3 | import { test } from 'node:test';
|
|---|
| 4 | import assert from 'node:assert/strict';
|
|---|
| 5 |
|
|---|
| 6 | process.env.DATABASE_PATH = ':memory:';
|
|---|
| 7 | process.env.PUBLIC_BASE_URL = 'https://test.example';
|
|---|
| 8 |
|
|---|
| 9 | const dbMod = await import('../src/config/database.js');
|
|---|
| 10 | dbMod.initializeDatabase();
|
|---|
| 11 | const AP = (await import('../src/services/ActivityPubService.js')).default;
|
|---|
| 12 |
|
|---|
| 13 | const base = 'https://test.example';
|
|---|
| 14 | const site = { slug: 'me', primary_slug: 'me' };
|
|---|
| 15 | const SECRET = 'THE-SECRET-BODY-that-must-not-federate';
|
|---|
| 16 |
|
|---|
| 17 | test('a paid post federates a teaser + supporters link, not the full content', () => {
|
|---|
| 18 | const post = {
|
|---|
| 19 | id: 'p1', slug: 'geheim', title: 'Geheim', tags: '[]',
|
|---|
| 20 | content: `<p>intro zin die de teaser wordt.</p><p>${SECRET}</p>`,
|
|---|
| 21 | excerpt: '', paid: 1, created_at: '2026-01-01T00:00:00Z', published_at: '2026-01-01T00:00:00Z',
|
|---|
| 22 | };
|
|---|
| 23 | const note = AP.buildNote(base, site, post);
|
|---|
| 24 | assert.equal(note.type, 'Note');
|
|---|
| 25 | assert.ok(note.content.includes('Lees de volledige post'), 'has the supporters link');
|
|---|
| 26 | assert.ok(note.content.includes(`${base}/geheim`), 'links back to the post');
|
|---|
| 27 | assert.ok(!note.content.includes(SECRET), 'the secret body must NOT federate');
|
|---|
| 28 | assert.deepEqual(note.to, ['https://www.w3.org/ns/activitystreams#Public'], 'teaser is public');
|
|---|
| 29 | assert.ok(!('attachment' in note) || !note.attachment || note.attachment.length === 0, 'no media leaks');
|
|---|
| 30 | });
|
|---|
| 31 |
|
|---|
| 32 | test('the excerpt is used as the teaser when present', () => {
|
|---|
| 33 | const post = {
|
|---|
| 34 | id: 'p2', slug: 'x', title: 'X', tags: '[]',
|
|---|
| 35 | content: `<p>${SECRET}</p>`, excerpt: 'Netjes teasertje', paid: 1,
|
|---|
| 36 | created_at: '2026-01-01T00:00:00Z',
|
|---|
| 37 | };
|
|---|
| 38 | const note = AP.buildNote(base, site, post);
|
|---|
| 39 | assert.ok(note.content.includes('Netjes teasertje'));
|
|---|
| 40 | assert.ok(!note.content.includes(SECRET));
|
|---|
| 41 | });
|
|---|
| 42 |
|
|---|
| 43 | test('a normal (non-paid) post still federates its full content', () => {
|
|---|
| 44 | const post = {
|
|---|
| 45 | id: 'p3', slug: 'open', title: 'Open', tags: '[]',
|
|---|
| 46 | content: `<p>${SECRET}</p>`, paid: 0, created_at: '2026-01-01T00:00:00Z',
|
|---|
| 47 | };
|
|---|
| 48 | const note = AP.buildNote(base, site, post);
|
|---|
| 49 | assert.ok(note.content.includes(SECRET), 'non-paid content federates as before');
|
|---|
| 50 | });
|
|---|