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

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

Elke collectie pagineert nu, via een helper in plaats van tien keer dezelfde regels

Vervolg op cb3001e, waar alleen de outbox eraan geloofde omdat Funkwhale daar
over viel. Robins opdracht: de rest ook, om compleetheid te garanderen. Dat is
de goede volgorde -- een foutmelding repareren waar hij valt laat de volgende
lezer op de volgende collectie stuklopen.

pagedCollection in ap-core, en daar hangen ze nu allemaal aan:

outbox followers following featured
tracks playlists playlist post-tracks
replies + de zeven guardianship-wachtrijen

De items blijven overal INLINE op de wortel; first en last wijzen naar dezelfde
pagina, want onze collecties zijn gekapt en er is er precies een. Wie ze vandaag
zonder pagineren leest -- Shaer doet dat -- merkt er niets van.

DRIE PLEKKEN DIE BEWUST AFWIJKEN, want een sleepnet is geen zorgvuldigheid:

  • de guardianship-wachtrijen krijgen hun @context van de route (queueRoute), dus daar staan de velden er met de hand bij. pagedCollection zou de context een tweede keer toevoegen.
  • de thread-collectie draagt al een ?object= in zijn id. Daar ?page= achteraan plakken pagineert niets, het herhaalt de vraag. Owner-only en door Shaer gelezen, dus geen federatiebelang.
  • followers en following geven publiek alleen een AANTAL. Die krijgen de velden juist wel: anders is "ik mag de lijst niet zien" niet te onderscheiden van een kapot antwoord -- dezelfde stille dubbelzinnigheid die we vandaag bij een ander aantroffen.

Vier tests erbij, waaronder een die bewaakt dat de helper attributedTo niet
opeet en een die bewaakt dat de wachtrijen GEEN eigen @context krijgen.

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

  • Property mode set to 100644
File size: 4.3 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});
64
65// ── En de andere collecties, want compleetheid was de opdracht ────────────
66
67test('followers en following dragen ze ook, ook count-only', () => {
68 // Publiek geven die alleen een AANTAL en houden de lijst dicht. Juist dan is
69 // een geldige collectie belangrijk: anders is "ik mag de lijst niet zien"
70 // niet te onderscheiden van een kapot antwoord.
71 const f = AP.buildFollowers(BASE, site, 42, null);
72 assert.equal(f.totalItems, 42);
73 assert.deepEqual(f.orderedItems, []);
74 assert.equal(f.first, `${BASE}/ap/users/dev/followers?page=1`);
75 const g = AP.buildFollowing(BASE, site, 3, null);
76 assert.equal(g.first, `${BASE}/ap/users/dev/following?page=1`);
77 assert.equal(g.last, `${BASE}/ap/users/dev/following?page=1`);
78});
79
80test('featured ook', () => {
81 const ft = AP.buildFeatured(BASE, site, [POST]);
82 assert.equal(ft.first, `${BASE}/ap/users/dev/featured?page=1`);
83 assert.equal(ft.orderedItems.length, 1, 'en de items blijven inline');
84});
85
86test('de muziekcollecties ook, met hun eigen velden erbij', () => {
87 const tc = AP.buildTrackCollection(BASE, site, []);
88 assert.equal(tc.first, `${BASE}/ap/users/dev/tracks?page=1`);
89 assert.equal(tc.attributedTo, `${BASE}/ap/users/dev`,
90 'de helper mag attributedTo niet opeten');
91});
92
93test('de guardianship-wachtrijen ook', async () => {
94 const q = await import('../src/services/guardianship/queues.js');
95 const c = q.helpCollection('https://ons.test/q/help', 'dev');
96 assert.equal(c.first, 'https://ons.test/q/help?page=1');
97 assert.equal(c['@context'], undefined,
98 'de context zet de route erop -- twee keer zou hem ongeldig maken');
99});
Note: See TracBrowser for help on using the repository browser.