| 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.
|
|---|
| 10 | import { test } from 'node:test';
|
|---|
| 11 | import assert from 'node:assert/strict';
|
|---|
| 12 | import http from 'http';
|
|---|
| 13 |
|
|---|
| 14 | process.env.DATABASE_PATH = ':memory:';
|
|---|
| 15 | process.env.PUBLIC_BASE_URL = 'https://klonkt.test';
|
|---|
| 16 |
|
|---|
| 17 | const ontvangen = []; // { pad, type }
|
|---|
| 18 | let status = 202;
|
|---|
| 19 | const 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 | });
|
|---|
| 32 | await new Promise((r) => ander.listen(0, '127.0.0.1', r));
|
|---|
| 33 | const host = `127.0.0.1:${ander.address().port}`;
|
|---|
| 34 | const inbox = `http://${host}/inbox`;
|
|---|
| 35 | process.env.AP_ALLOW_HOSTS = host;
|
|---|
| 36 | test.after(() => ander.close());
|
|---|
| 37 |
|
|---|
| 38 | const dbMod = await import('../src/config/database.js');
|
|---|
| 39 | const db = dbMod.default;
|
|---|
| 40 | { const stil = console.log; console.log = () => {}; try { dbMod.initializeDatabase(); } finally { console.log = stil; } }
|
|---|
| 41 | const AP = await import('../src/services/ActivityPubService.js');
|
|---|
| 42 |
|
|---|
| 43 | db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)').run('u1', 'robin', 'r@test', 'x', 'god');
|
|---|
| 44 | db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run('s1', 'me', 'Me', 'u1');
|
|---|
| 45 | const site = db.prepare('SELECT * FROM sites WHERE slug = ?').get('me');
|
|---|
| 46 | db.prepare('INSERT INTO ap_followers (slug, actor_uri, inbox) VALUES (?,?,?)').run('me', `http://${host}/users/volger`, inbox);
|
|---|
| 47 | const 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 |
|
|---|
| 53 | const rij = () => db.prepare('SELECT * FROM ap_delivery ORDER BY id').all();
|
|---|
| 54 | const typen = () => rij().map((r) => JSON.parse(r.body).type);
|
|---|
| 55 | const leeg = () => { db.prepare('DELETE FROM ap_delivery').run(); ontvangen.length = 0; status = 202; };
|
|---|
| 56 | const 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 |
|
|---|
| 58 | test('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 |
|
|---|
| 76 | test('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 |
|
|---|
| 85 | test('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 |
|
|---|
| 93 | test('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 |
|
|---|
| 104 | test('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 | }));
|
|---|