source: Klonkt/test/follow-accept-match.test.js@ 06bae1e

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

Een Accept die ons follow-id niet teruggeeft, telt nu ook

Gevonden tegen Funkwhale (audio.pepemoss.com, 7 augustus). Wij stuurden een
Follow met id

.../ap/users/dev#follow-1786161977286-bb2de32f

en de Accept kwam terug met een door Funkwhale ZELF verzonnen id, in onze
eigen namespace:

.../ap/users/dev#follows/19fd8b00-8f66-4362-b233-542eadfa40fc

De update matchte op follow_id en raakte dus niets. De rij bleef eeuwig op
'pending' en er kwam nooit iets binnen -- terwijl de logregel doodleuk
'follow accepted' riep. Een stille no-op, en precies daarom viel het pas op
toen de muziek uitbleef.

Twee dingen veranderd:

TERUGVAL raakt het follow-id niets, dan telt het paar dat we wel zeker

weten: (deze site, deze actor), en alleen als die rij nog op
pending staat. De Accept is handtekening-geverifieerd en
actorUri is de ondertekenaar, dus dit kan niets anders openzetten
dan een follow die wij zelf verstuurd hebben.

EERLIJK de logregel zegt nu 'accept UNMATCHED' als er niets geraakt is,
LOGGEN met het ontvangen id erbij. Zonder dat was dit onvindbaar.

5 tests, waaronder de Funkwhale-vorm en twee die bewaken dat een vreemde
Accept niet andermans rij kan openzetten.

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

  • Property mode set to 100644
File size: 3.7 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('een Accept van een ANDERE actor raakt onze rij niet', async () => {
67 zetPending();
68 await AP.handleInbox({
69 body: { type: 'Accept', actor: 'https://elders.example/users/vreemd', object: { id: 'https://elders.example/x', type: 'Follow' } },
70 headers: {}, get: () => undefined, socket: {},
71 }, 'dev', { id: 'https://elders.example/users/vreemd' });
72 assert.equal(status(), 'pending', 'alleen de ondertekenaar zelf kan zijn eigen follow openzetten');
73});
74
75test('een al geaccepteerde rij wordt niet opnieuw geraakt door een vreemde Accept', async () => {
76 zetPending();
77 db.prepare('UPDATE ap_following SET status = ?').run('accepted');
78 await accept({ id: 'https://test.example/ap/users/dev#follows/anders', type: 'Follow', object: REMOTE });
79 assert.equal(status(), 'accepted');
80});
Note: See TracBrowser for help on using the repository browser.