source: Klonkt/test/ap-outbox-paging.test.js@ cb3001e

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

De outbox pagineert, want zonder first en last weigerde Funkwhale hem

De eerste CONCRETE reden die we hoorden waarom er niets van ons binnenkwam. Uit
hun log:

Error while fetching actor outbox: https://dev.klonkt.com/ap/users/dev/outbox
ValidationError: {'first': This field is required.,

'last': This field is required.}

AS2 eist die velden niet -- een OrderedCollection mag zijn items inline dragen,
en dat deden wij -- maar bijna iedereen pagineert, en een lezer die de
paginaweg volgt liep hier dood. Dagenlang zochten we het bij onze objecten
terwijl het de omslag was.

De items blijven WEL inline op de wortel. Shaer bouwt zijn feed daaruit, en wie
hem vandaag leest hoort er morgen niet voor te hoeven pagineren. Er is precies
een pagina (we kappen op MAX_OUTBOX), dus first en last wijzen naar dezelfde, en
?page=1 geeft een OrderedCollectionPage met partOf terug.

Ook de LEGE outbox van een geblokkeerde lezer draagt ze nu -- juist die zou
anders door dezelfde serializer geweigerd worden, en dan is een blokkade van
buiten niet te onderscheiden van een kapotte server.

Vijf tests. Anker bij het bewerken was .slice(0, MAX_OUTBOX): het retourblok van
buildOutbox is teken voor teken gelijk aan dat van buildFeatured, en een
patroon zonder dat anker raakte er twee.

NIET meegenomen: followers, following en featured missen first/last ook. Daar
klaagde niemand over en Mastodon leest ze zonder morren, dus dat is een aparte
afweging en geen sleepnet.

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

  • Property mode set to 100644
File size: 2.7 KB
Line 
1// De outbox pagineert (Funkwhale, 11-8).
2//
3// Hun serializer weigerde onze outbox letterlijk met
4// {'first': ['This field is required.'], 'last': ['This field is required.']}
5// AS2 eist die velden niet -- een collectie mag zijn items inline dragen -- maar
6// bijna iedereen pagineert, en dit was de eerste CONCRETE reden die we hoorden
7// waarom er niets van ons binnenkwam.
8//
9// De items blijven inline op de wortel: Shaer bouwt zijn feed daaruit.
10import { test } from 'node:test';
11import assert from 'node:assert/strict';
12
13process.env.DATABASE_PATH = ':memory:';
14process.env.PUBLIC_BASE_URL = 'https://ons.test';
15
16const dbMod = await import('../src/config/database.js');
17const db = dbMod.default;
18dbMod.initializeDatabase();
19const AP = await import('../src/services/ActivityPubService.js');
20
21db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
22 .run('u1', 'u1', 'u1@t', 'x', 'god');
23db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run('s1', 'dev', 'Dev', 'u1');
24const site = db.prepare("SELECT * FROM sites WHERE id = 's1'").get();
25const POST = { id: 'p1', slug: 'hallo', title: 'Hallo', content: '<p>x</p>', published_at: '2026-08-01T00:00:00Z' };
26const BASE = 'https://ons.test';
27const OUT = `${BASE}/ap/users/dev/outbox`;
28
29test('de wortel draagt first en last', () => {
30 const ob = AP.buildOutbox(BASE, site, [POST]);
31 assert.equal(ob.type, 'OrderedCollection');
32 assert.equal(ob.first, `${OUT}?page=1`);
33 assert.equal(ob.last, `${OUT}?page=1`);
34});
35
36test('en houdt zijn items inline, want Shaer leest die', () => {
37 const ob = AP.buildOutbox(BASE, site, [POST]);
38 assert.equal(ob.orderedItems.length, 1);
39 assert.equal(ob.totalItems, 1);
40});
41
42test('de pagina is een OrderedCollectionPage die terugwijst', () => {
43 const pg = AP.buildOutbox(BASE, site, [POST], [], { page: true });
44 assert.equal(pg.type, 'OrderedCollectionPage');
45 assert.equal(pg.id, `${OUT}?page=1`);
46 assert.equal(pg.partOf, OUT, 'zonder partOf hangt een pagina in de lucht');
47 assert.equal(pg.orderedItems.length, 1);
48});
49
50test('een pagina noemt zichzelf geen wortel', () => {
51 const pg = AP.buildOutbox(BASE, site, [POST], [], { page: true });
52 assert.equal(pg.first, undefined, 'first hoort op de wortel, niet op de pagina');
53 assert.equal(pg.last, undefined);
54});
55
56test('ook een LEGE outbox draagt ze -- een geblokkeerde lezer krijgt geldige AS2', () => {
57 // De blocked-tak levert een lege collectie. Zonder first/last zou juist die
58 // door dezelfde serializer geweigerd worden.
59 const leeg = AP.buildOutbox(BASE, site, []);
60 assert.equal(leeg.totalItems, 0);
61 assert.equal(leeg.first, `${OUT}?page=1`);
62 assert.equal(leeg.last, `${OUT}?page=1`);
63});
Note: See TracBrowser for help on using the repository browser.