source: Klonkt/test/escalation-teapot.test.js@ 783b9ff

main
Last change on this file since 783b9ff was 3d882bd, checked in by Bart <bart@…>, 5 weeks ago

FEP-633c §4.1: een kapotte guardian kost een kind niet de goede

Een "guardian" met eigen guardians is er geen (§1), en een escalatie daarheen
komt nergens aan: er is geen grand-guardian om naar door te vertakken. Klonkt
handhaafde dat nergens. De daemon doet het vanaf het begin, en dat verschil is
precies waar shaer-6d9 voor bestaat.

Nu zacht falen zoals §4.1 vraagt: dat ene doelwit valt af, de rest krijgt de
hulpvraag gewoon. Andersom zou één verkeerd geconfigureerd account van een
volwassene de noodroep van een kind helemaal laten mislukken.

Alleen bij een hulpvraag. Een gewoon direct bericht is geen escalatie, en een
ward mag een andere ward best iets sturen — daar stilletjes ontvangers uit
slopen zou een bug zijn met een spec-verwijzing eromheen.

Als ELKE guardian kapot is, is er niets om naar door te leveren. §4 dekt dat
niet, want §4.1 gaat ervan uit dat er anderen zijn. Dan komt de hulpvraag bij
niemand aan, en dat is het enige wat deze FEP juist moet voorkomen: dat faalt
dus luid in de log in plaats van een aflevering te melden die niet gebeurde.

carriesGuardians() staat nu in context.js, waar de rest van het vocabulaire ook
woont: §3 en §5.2 stellen dezelfde vraag en moeten hem hetzelfde lezen.

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

  • Property mode set to 100644
File size: 3.6 KB
Line 
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.
12import { test } from 'node:test';
13import assert from 'node:assert/strict';
14
15process.env.DATABASE_PATH = ':memory:';
16process.env.PUBLIC_BASE_URL = 'https://test.example';
17
18const dbMod = await import('../src/config/database.js');
19const db = dbMod.default;
20dbMod.initializeDatabase();
21const AP = (await import('../src/services/ActivityPubService.js')).default;
22
23const BASE = 'https://test.example';
24const local = (slug) => `${BASE}/ap/users/${slug}`;
25
26db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
27 .run('u1', 'u1', 'u1@test', 'x', 'god');
28let n = 0;
29function 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}
34const guards = (slug, other) =>
35 db.prepare("INSERT INTO ap_guardianships (slug, role, other_uri, status) VALUES (?, 'ward', ?, 'accepted')")
36 .run(slug, other);
37
38const kid = site('kid'); // the ward calling for help
39site('good'); // a proper guardian
40site('bad'); // listed as a guardian, but a ward itself
41site('gran'); // who guards `bad`
42
43guards('kid', local('good'));
44guards('kid', local('bad'));
45guards('bad', local('gran')); // this is what makes `bad` malformed as a guardian
46
47const help = await AP.deliverDirectNote(kid, {
48 recipients: [local('good'), local('bad')],
49 text: 'ik snap dit niet', helpRequest: true,
50});
51
52test('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
60test('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
70test('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});
Note: See TracBrowser for help on using the repository browser.