| 1 | // FEP-633c §4.1 — a malformed guardian must not cost a child the good ones.
|
|---|
| 2 | //
|
|---|
| 3 | // A "guardian" that carries guardians of its own is not one (§1). When a ward
|
|---|
| 4 | // calls for help, an escalation addressed to such an actor goes nowhere: there
|
|---|
| 5 | // is no grand-guardian to recurse to. The spec's answer is to fail SOFTLY —
|
|---|
| 6 | // drop that one target, keep delivering to the rest — because the alternative
|
|---|
| 7 | // is a child whose call for help fails entirely because one adult's account is
|
|---|
| 8 | // misconfigured.
|
|---|
| 9 | //
|
|---|
| 10 | // Klonkt enforced this nowhere until now; the daemon has had it since the
|
|---|
| 11 | // beginning, which is the divergence shaer-6d9 exists to catch.
|
|---|
| 12 | import { test } from 'node:test';
|
|---|
| 13 | import assert from 'node:assert/strict';
|
|---|
| 14 |
|
|---|
| 15 | process.env.DATABASE_PATH = ':memory:';
|
|---|
| 16 | process.env.PUBLIC_BASE_URL = 'https://test.example';
|
|---|
| 17 |
|
|---|
| 18 | const dbMod = await import('../src/config/database.js');
|
|---|
| 19 | const db = dbMod.default;
|
|---|
| 20 | dbMod.initializeDatabase();
|
|---|
| 21 | const AP = (await import('../src/services/ActivityPubService.js')).default;
|
|---|
| 22 |
|
|---|
| 23 | const BASE = 'https://test.example';
|
|---|
| 24 | const local = (slug) => `${BASE}/ap/users/${slug}`;
|
|---|
| 25 |
|
|---|
| 26 | db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
|
|---|
| 27 | .run('u1', 'u1', 'u1@test', 'x', 'god');
|
|---|
| 28 | let n = 0;
|
|---|
| 29 | function site(slug) {
|
|---|
| 30 | db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_primary) VALUES (?,?,?,?,?)')
|
|---|
| 31 | .run(`s${++n}`, slug, slug, 'u1', n === 1 ? 1 : 0);
|
|---|
| 32 | return db.prepare('SELECT * FROM sites WHERE slug = ?').get(slug);
|
|---|
| 33 | }
|
|---|
| 34 | const guards = (slug, other) =>
|
|---|
| 35 | db.prepare("INSERT INTO ap_guardianships (slug, role, other_uri, status) VALUES (?, 'ward', ?, 'accepted')")
|
|---|
| 36 | .run(slug, other);
|
|---|
| 37 |
|
|---|
| 38 | const kid = site('kid'); // the ward calling for help
|
|---|
| 39 | site('good'); // a proper guardian
|
|---|
| 40 | site('bad'); // listed as a guardian, but a ward itself
|
|---|
| 41 | site('gran'); // who guards `bad`
|
|---|
| 42 |
|
|---|
| 43 | guards('kid', local('good'));
|
|---|
| 44 | guards('kid', local('bad'));
|
|---|
| 45 | guards('bad', local('gran')); // this is what makes `bad` malformed as a guardian
|
|---|
| 46 |
|
|---|
| 47 | const help = await AP.deliverDirectNote(kid, {
|
|---|
| 48 | recipients: [local('good'), local('bad')],
|
|---|
| 49 | text: 'ik snap dit niet', helpRequest: true,
|
|---|
| 50 | });
|
|---|
| 51 |
|
|---|
| 52 | test('an escalation skips the malformed guardian and still reaches the others (§4.1)', () => {
|
|---|
| 53 | assert.ok(help && help.id, 'the note was built');
|
|---|
| 54 | assert.deepEqual(help.teapots, [local('bad')],
|
|---|
| 55 | 'the dropped target is named — SHOULD log the condition, not swallow it');
|
|---|
| 56 | assert.equal(help.delivered, 1,
|
|---|
| 57 | 'and the well-formed guardian still got it: a malformed target MUST NOT block the others');
|
|---|
| 58 | });
|
|---|
| 59 |
|
|---|
| 60 | test('an ordinary direct note is not an escalation, so nobody is dropped', async () => {
|
|---|
| 61 | // §4.1 is about escalations. A ward messaging another ward is ordinary
|
|---|
| 62 | // conversation, and silently dropping recipients from it would be a bug
|
|---|
| 63 | // wearing a spec reference.
|
|---|
| 64 | const chat = await AP.deliverDirectNote(kid, { recipients: [local('bad')], text: 'hoi' });
|
|---|
| 65 | assert.ok(chat && chat.id);
|
|---|
| 66 | assert.deepEqual(chat.teapots, []);
|
|---|
| 67 | assert.equal(chat.delivered, 1, 'delivered to a ward, because that is allowed');
|
|---|
| 68 | });
|
|---|
| 69 |
|
|---|
| 70 | test('a ward whose every guardian is malformed calls out into nothing', async () => {
|
|---|
| 71 | // §4 does not cover this, because §4.1 assumes there are others to continue
|
|---|
| 72 | // to. There are not. The call reaches no one, which is the single outcome
|
|---|
| 73 | // this FEP exists to prevent — so it fails loudly in the log rather than
|
|---|
| 74 | // reporting a delivery that did not happen.
|
|---|
| 75 | const orphan = site('orphan');
|
|---|
| 76 | guards('orphan', local('bad'));
|
|---|
| 77 | const nowhere = await AP.deliverDirectNote(orphan, {
|
|---|
| 78 | recipients: [local('bad')], text: 'help', helpRequest: true,
|
|---|
| 79 | });
|
|---|
| 80 | assert.equal(nowhere, null, 'no note, no false "delivered"');
|
|---|
| 81 | });
|
|---|