source: Klonkt/test/follow-accept-match.test.js

main
Last change on this file was 0517013, checked in by Robin <roboburr@…>, 5 weeks ago

De Accept-terugval werkt nu ook op de gedeelde inbox

Vervolg op 06bae1e, en de fout die daar in zat: de terugval hing aan
slugParam, en die is LEEG wanneer er op /ap/inbox wordt afgeleverd in plaats
van op /ap/users/<slug>/inbox. Funkwhale doet precies dat, dus de terugval
werd overgeslagen en de rij bleef alsnog op pending -- alleen zei de log nu
tenminste eerlijk "accept UNMATCHED".

Wie wij zijn staat gewoon in de ingesloten Follow: die hebben wij zelf
verstuurd, dus object.actor is onze eigen actor-URI. Daar leiden we de slug
uit af als slugParam ontbreekt.

Zonder bruikbare Follow raden we niets: dan blijft het pending. Er is een test
die dat vastlegt, naast de test voor de gedeelde-inbox-weg zelf.

7 tests.

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

  • Property mode set to 100644
File size: 4.8 KB
Line 
1// Een Accept op onze Follow moet de volgrelatie openzetten -- ook als de
2// tegenpartij ons follow-id niet teruggeeft.
3//
4// Aanleiding: Funkwhale (audio.pepemoss.com, 7 augustus). Wij stuurden een
5// Follow met id `<actor>#follow-<ts>-<rnd>`; de Accept kwam terug met een door
6// Funkwhale zelf verzonnen id in onze namespace, `<actor>#follows/<uuid>`.
7// Matchen op follow_id raakte niets, de rij bleef op 'pending', en de logregel
8// riep toch 'accepted'. De relatie kwam nooit tot stand en er kwam dus ook
9// nooit muziek binnen.
10import { test } from 'node:test';
11import assert from 'node:assert/strict';
12
13process.env.DATABASE_PATH = ':memory:';
14process.env.PUBLIC_BASE_URL = 'https://test.example';
15
16const dbMod = await import('../src/config/database.js');
17const db = dbMod.default;
18dbMod.initializeDatabase();
19const AP = (await import('../src/services/ActivityPubService.js')).default;
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, is_public) VALUES (?,?,?,?,1)')
24 .run('s1', 'dev', 'Dev', 'u1');
25
26const REMOTE = 'https://audio.example/federation/actors/kanaal';
27const ONS_ID = 'https://test.example/ap/users/dev#follow-1786161977286-bb2de32f';
28
29function zetPending(followId = ONS_ID, actor = REMOTE) {
30 db.prepare('DELETE FROM ap_following').run();
31 db.prepare(`INSERT INTO ap_following (slug, actor_uri, handle, name, icon, url, inbox, follow_id, status, auto_boost, created_at)
32 VALUES (?,?,?,?,?,?,?,?,'pending',0,CURRENT_TIMESTAMP)`)
33 .run('dev', actor, '@kanaal@audio.example', 'Kanaal', null, null, actor + '/inbox', followId);
34}
35const status = (actor = REMOTE) =>
36 (db.prepare('SELECT status FROM ap_following WHERE slug = ? AND actor_uri = ?').get('dev', actor) || {}).status;
37
38// handleInbox draait de handtekeningcontrole; die is hier niet het onderwerp.
39// De Accept-tak krijgt een geverifieerde actor mee, precies zoals in bedrijf.
40const accept = (object) => AP.handleInbox({
41 body: { '@context': 'https://www.w3.org/ns/activitystreams', type: 'Accept', actor: REMOTE, object },
42 headers: {}, get: () => undefined, socket: {},
43}, 'dev', { id: REMOTE });
44
45test('de gewone weg: de Accept geeft ONS follow-id terug', async () => {
46 zetPending();
47 await accept({ id: ONS_ID, type: 'Follow', actor: 'https://test.example/ap/users/dev', object: REMOTE });
48 assert.equal(status(), 'accepted');
49});
50
51test('de Funkwhale-vorm: een zelfverzonnen id in onze namespace', async () => {
52 zetPending();
53 await accept({
54 id: 'https://test.example/ap/users/dev#follows/19fd8b00-8f66-4362-b233-542eadfa40fc',
55 type: 'Follow', actor: 'https://test.example/ap/users/dev', object: REMOTE,
56 });
57 assert.equal(status(), 'accepted', 'de terugval op (site, actor) hoort dit op te vangen');
58});
59
60test('een Accept als kale string blijft werken', async () => {
61 zetPending();
62 await accept(ONS_ID);
63 assert.equal(status(), 'accepted');
64});
65
66test('bezorgd op de GEDEELDE inbox: de slug komt uit de ingesloten Follow', async () => {
67 // Funkwhale levert af op /ap/inbox, niet op /ap/users/<slug>/inbox. Dan is
68 // slugParam null en moeten we uit `object.actor` afleiden wie wij zijn --
69 // die Follow hebben wij immers zelf verstuurd.
70 zetPending();
71 await AP.handleInbox({
72 body: {
73 type: 'Accept', actor: REMOTE,
74 object: {
75 id: 'https://test.example/ap/users/dev#follows/389cf422-7e38-4bad-858e-35def4c3e2e9',
76 type: 'Follow', actor: 'https://test.example/ap/users/dev', object: REMOTE,
77 },
78 },
79 headers: {}, get: () => undefined, socket: {},
80 }, null, { id: REMOTE });
81 assert.equal(status(), 'accepted');
82});
83
84test('op de gedeelde inbox zonder bruikbare Follow blijft het pending', async () => {
85 zetPending();
86 await AP.handleInbox({
87 body: { type: 'Accept', actor: REMOTE, object: { id: 'https://elders.example/x', type: 'Follow' } },
88 headers: {}, get: () => undefined, socket: {},
89 }, null, { id: REMOTE });
90 assert.equal(status(), 'pending', 'zonder te weten wie wij zijn, raden we niet');
91});
92
93test('een Accept van een ANDERE actor raakt onze rij niet', async () => {
94 zetPending();
95 await AP.handleInbox({
96 body: { type: 'Accept', actor: 'https://elders.example/users/vreemd', object: { id: 'https://elders.example/x', type: 'Follow' } },
97 headers: {}, get: () => undefined, socket: {},
98 }, 'dev', { id: 'https://elders.example/users/vreemd' });
99 assert.equal(status(), 'pending', 'alleen de ondertekenaar zelf kan zijn eigen follow openzetten');
100});
101
102test('een al geaccepteerde rij wordt niet opnieuw geraakt door een vreemde Accept', async () => {
103 zetPending();
104 db.prepare('UPDATE ap_following SET status = ?').run('accepted');
105 await accept({ id: 'https://test.example/ap/users/dev#follows/anders', type: 'Follow', object: REMOTE });
106 assert.equal(status(), 'accepted');
107});
Note: See TracBrowser for help on using the repository browser.