source: Klonkt/test/direct-sends-queue.test.js@ bfa3ba3

main
Last change on this file since bfa3ba3 was bfa3ba3, checked in by Robin <roboburr@…>, 25 hours ago

Ook de directe verzenders langs de volgorde per inbox

Rest van shaer-xzab.3. Drie plekken verstuurden zelf met deliver() en zetten
pas bij een mislukking in de rij: een antwoord (deliverReply), het wissen
daarvan (deliverOutboxDelete) en een direct bericht (deliverDirectNote, ook de
hulpvraag en de zwaai). Zo kon de Delete van een antwoord aankomen vóór het
antwoord zelf als dat nog in de rij wachtte, en stond het antwoord daarna weer
op die server.

Nu één weg: deliverNowOrQueue in ap-transport, met dezelfde regel als
deliverWithRetry (wacht er voor die inbox al iets, of zit de server in zijn
backoff, dan achteraan), en hij zegt of het 'sent' of 'queued' werd, want
deliverReply en deliverDirectNote tellen wat er meteen aankwam.
deliverWithRetry is er nu een dunne laag over. guardianship/delivery.js krijgt
deliverNowOrQueue via wireDelivery in plaats van deliver en enqueueDelivery.

test/direct-sends-queue.test.js: vijf toetsen tegen een echte HTTP-server,
waaronder het hele scenario: antwoord lukt niet en wacht, de server is terug,
het antwoord wordt gewist -- de Delete wacht achter de Create en ze komen in
die volgorde aan. Vier mutaties apart gedraaid (elke plek terug naar zelf
versturen, en 'sent' dat niet telt), elk gevangen. Volledige suite 1376 groen.

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

  • Property mode set to 100644
File size: 5.4 KB
Line 
1// Ook de directe verzenders gaan langs de volgorde per inbox (shaer-xzab.3).
2//
3// Drie plekken verstuurden zelf met deliver() en zetten pas bij een mislukking
4// in de rij: een antwoord (deliverReply), het wissen daarvan
5// (deliverOutboxDelete) en een direct bericht (deliverDirectNote, ook de
6// hulpvraag). Zo kon een Delete aankomen vóór de Create die nog in de rij
7// wachtte -- en stond het antwoord daarna weer op die server.
8//
9// Tegen echte HTTP-servers op 127.0.0.1, toegelaten via AP_ALLOW_HOSTS.
10import { test } from 'node:test';
11import assert from 'node:assert/strict';
12import http from 'http';
13
14process.env.DATABASE_PATH = ':memory:';
15process.env.PUBLIC_BASE_URL = 'https://klonkt.test';
16
17const ontvangen = []; // { pad, type }
18let status = 202;
19const ander = http.createServer((req, res) => {
20 let body = '';
21 req.on('data', (c) => { body += c; });
22 req.on('end', () => {
23 const host = `http://${req.headers.host}`;
24 if (req.method === 'GET' && req.url === '/users/oma') {
25 res.setHeader('Content-Type', 'application/activity+json');
26 return res.end(JSON.stringify({ id: `${host}/users/oma`, type: 'Person', preferredUsername: 'oma', inbox: `${host}/users/oma/inbox` }));
27 }
28 try { const a = JSON.parse(body); ontvangen.push({ pad: req.url, type: a.type }); } catch { /* geen json */ }
29 res.statusCode = status; res.end();
30 });
31});
32await new Promise((r) => ander.listen(0, '127.0.0.1', r));
33const host = `127.0.0.1:${ander.address().port}`;
34const inbox = `http://${host}/inbox`;
35process.env.AP_ALLOW_HOSTS = host;
36test.after(() => ander.close());
37
38const dbMod = await import('../src/config/database.js');
39const db = dbMod.default;
40{ const stil = console.log; console.log = () => {}; try { dbMod.initializeDatabase(); } finally { console.log = stil; } }
41const AP = await import('../src/services/ActivityPubService.js');
42
43db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)').run('u1', 'robin', 'r@test', 'x', 'god');
44db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run('s1', 'me', 'Me', 'u1');
45const site = db.prepare('SELECT * FROM sites WHERE slug = ?').get('me');
46db.prepare('INSERT INTO ap_followers (slug, actor_uri, inbox) VALUES (?,?,?)').run('me', `http://${host}/users/volger`, inbox);
47const parent = {
48 id: 1, post_id: '', actor_uri: 'https://unresolvable.invalid/u/ness',
49 actor_url: 'https://unresolvable.invalid/@ness', actor_handle: '@ness@unresolvable.invalid',
50 object_uri: 'https://unresolvable.invalid/notes/9',
51};
52
53const rij = () => db.prepare('SELECT * FROM ap_delivery ORDER BY id').all();
54const typen = () => rij().map((r) => JSON.parse(r.body).type);
55const leeg = () => { db.prepare('DELETE FROM ap_delivery').run(); ontvangen.length = 0; status = 202; };
56const stil = (f) => async () => { const l = console.log, w = console.warn; console.log = () => {}; console.warn = () => {}; try { await f(); } finally { console.log = l; console.warn = w; } };
57
58test('een gewist antwoord komt niet aan vóór het antwoord zelf', stil(async () => {
59 leeg();
60 status = 503; // de server van de volger hapert
61 const r = await AP.deliverReply(site, { postId: '', postSlug: null, parent, text: 'eerst' });
62 assert.equal(r.delivered, 0);
63 assert.deepEqual(typen(), ['Create'], 'het antwoord wacht in de rij');
64
65 status = 202; // hij is er weer, de rij is nog niet geweest
66 ontvangen.length = 0;
67 await AP.deliverOutboxDelete(site, r.id);
68 assert.deepEqual(ontvangen, [], 'de Delete gaat niet voor');
69 assert.deepEqual(typen(), ['Create', 'Delete'], 'hij wacht achter de Create');
70
71 await AP.processDeliveryQueue();
72 await AP.processDeliveryQueue();
73 assert.deepEqual(ontvangen.map((o) => o.type), ['Create', 'Delete'], 'en zo komen ze aan');
74}));
75
76test('een antwoord gaat niet voorbij wat er voor die inbox al wacht', stil(async () => {
77 leeg();
78 AP.enqueueDelivery('me', inbox, { type: 'Create', id: 'https://klonkt.test/eerder' });
79 const r = await AP.deliverReply(site, { postId: '', postSlug: null, parent, text: 'later' });
80 assert.equal(r.delivered, 0, 'niet als verstuurd geteld');
81 assert.deepEqual(ontvangen, []);
82 assert.equal(rij().length, 2);
83}));
84
85test('zonder wachtrij gaat een antwoord gewoon meteen, en telt', stil(async () => {
86 leeg();
87 const r = await AP.deliverReply(site, { postId: '', postSlug: null, parent, text: 'meteen' });
88 assert.equal(r.delivered, 1);
89 assert.deepEqual(ontvangen.map((o) => o.type), ['Create']);
90 assert.deepEqual(rij(), []);
91}));
92
93test('een direct bericht gaat niet voorbij wat er voor die inbox al wacht', stil(async () => {
94 leeg();
95 const omaInbox = `http://${host}/users/oma/inbox`;
96 AP.enqueueDelivery('me', omaInbox, { type: 'Create', id: 'https://klonkt.test/eerder' });
97 const r = await AP.deliverDirectNote(site, { recipients: [`http://${host}/users/oma`], text: 'hoi oma' });
98 assert.ok(r, 'het bericht is gemaakt');
99 assert.equal(r.delivered, 0);
100 assert.deepEqual(ontvangen, [], 'niets voorbij de wachtende');
101 assert.deepEqual(rij().map((x) => x.inbox), [omaInbox, omaInbox]);
102}));
103
104test('zonder wachtrij gaat een direct bericht meteen, en telt', stil(async () => {
105 leeg();
106 const r = await AP.deliverDirectNote(site, { recipients: [`http://${host}/users/oma`], text: 'hoi oma, nu' });
107 assert.equal(r.delivered, 1);
108 assert.deepEqual(ontvangen.map((o) => o.type), ['Create']);
109}));
Note: See TracBrowser for help on using the repository browser.