Changeset ffb371c in Klonkt for test


Ignore:
Timestamp:
08/08/2026 05:25:06 AM (5 weeks ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
34e63f0
Parents:
2220c49
git-author:
Robin <roboburr@…> (08/08/2026 05:25:04 AM)
git-committer:
roboburr <roboburr@…> (08/08/2026 05:25:06 AM)
Message:

Antwoorden is ook iets: een eigen poort (shaer-r4c)

Bart, 8-8: "Ook meedoen aan een gesprek valt onder een gate, maak die en maak die
zichtbaar."

HIER STOND EEN AANNAME, EN DIE WAS VAN MIJ. compose liet een antwoord bewust
door, met als reden "een antwoord is meedoen aan een gesprek, geen eigen podium".
Dat is een ontwerpkeuze die ik als vanzelfsprekend had opgeschreven, en Bart
draait hem terug: meedoen aan een gesprek is ook iets waar guardians over gaan.

EEN EIGEN POORT, NIET ONDER COMPOSE. Los in beide richtingen, en daar staan
toetsen op: je kunt willen dat een kind meepraat zonder eigen podium, en ook
precies andersom. Zou compose antwoorden meesluiten, dan is de nieuwe rij in het
paneel een knop die niets doet.

DE DEUR NAAST DE POORT. De innamepoort in ingestOutboxActivity dekt alleen C2S --
Shaer. routes/posts.js roept deliverReply op drie plekken RECHTSTREEKS aan, dus
de eigen webinterface van Klonkt loopt er nooit langs. Een poort die alleen in de
app staat is geen poort. De echte controle staat nu in deliverReply zelf, het
knooppunt dat beide paden delen; de outbox houdt zijn eigen check zodat de app
een nette 403 gated_replies krijgt in plaats van een stille null.

De boei komt daar niet langs en dat is geen toeval: een hulpvraag is altijd
direct en loopt via deliverDirectNote. Er is dus geen uitzondering nodig om hem
open te houden -- maar er staat wel een toets op, want dit is het gevaarlijkste
dat deze poort kan doen: een kind dat om hulp vraagt in het draadje waar het
misgaat.

Een direct antwoord wordt door allebei de poorten gedekt (messages en replies).
Een prive-antwoord is allebei, en dan mag allebei hem tegenhouden.

Paneel en shaer:capabilities krijgen hem gratis uit de catalogus. Labels in
nl/en/de. Zes toetsen, drie mutaties gecontroleerd. Suite 646/646.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • test/gates-family.test.js

    r2220c49 rffb371c  
    8484});
    8585
     86// ── Antwoorden: een eigen poort (shaer-r4c) ─────────────────────────────
     87//
     88// Hier stond de aanname dat een antwoord meedoen is en geen eigen podium, en
     89// dus onder compose door mocht. Bart heeft die teruggedraaid: meedoen aan een
     90// gesprek valt ook onder een poort. Wat deze toetsen bewaken is dat het een
     91// EIGEN poort is en geen aanhangsel van compose -- anders is hij in het paneel
     92// wel te zien maar niet los te zetten.
     93
     94test('replies dicht weigert een antwoord', async () => {
     95  db.prepare("UPDATE sites SET gate_replies = 0 WHERE slug = 'kind'").run();
     96  const reply = { type: 'Create', object: { type: 'Note', content: '<p>ja!</p>', inReplyTo: 'https://elders/x', to: ['https://www.w3.org/ns/activitystreams#Public'] } };
     97  const uit = await AP.ingestOutboxActivity(site(), user, reply);
     98  assert.equal(uit.status, 403);
     99  assert.equal(uit.error, 'gated_replies');
     100  db.prepare("UPDATE sites SET gate_replies = NULL WHERE slug = 'kind'").run();
     101});
     102
     103test('replies dicht laat een EIGEN post staan', async () => {
     104  // Los van compose, en dat is de hele reden dat het een eigen rij is: je kunt
     105  // willen dat een kind een podium heeft zonder in vreemde draadjes te duiken.
     106  db.prepare("UPDATE sites SET gate_replies = 0 WHERE slug = 'kind'").run();
     107  const post = { type: 'Create', object: { type: 'Note', content: '<p>hoi</p>', to: ['https://www.w3.org/ns/activitystreams#Public'] } };
     108  const uit = await AP.ingestOutboxActivity(site(), user, post);
     109  assert.notEqual(uit.error, 'gated_replies');
     110  assert.notEqual(uit.error, 'gated_compose');
     111  db.prepare("UPDATE sites SET gate_replies = NULL WHERE slug = 'kind'").run();
     112});
     113
     114test('compose dicht laat een antwoord staan zolang replies open is', async () => {
     115  // De andere richting van dezelfde onafhankelijkheid. Zou compose ook
     116  // antwoorden dichtzetten, dan is de nieuwe rij een knop die niets doet.
     117  db.prepare("UPDATE sites SET gate_compose = 0, gate_replies = 1 WHERE slug = 'kind'").run();
     118  const reply = { type: 'Create', object: { type: 'Note', content: '<p>ja!</p>', inReplyTo: 'https://elders/x', to: ['https://www.w3.org/ns/activitystreams#Public'] } };
     119  const uit = await AP.ingestOutboxActivity(site(), user, reply);
     120  assert.notEqual(uit.error, 'gated_compose');
     121  assert.notEqual(uit.error, 'gated_replies');
     122  db.prepare("UPDATE sites SET gate_compose = NULL, gate_replies = NULL WHERE slug = 'kind'").run();
     123});
     124
     125test('replies dicht houdt ook een DIRECT antwoord tegen', async () => {
     126  // Een prive-antwoord is allebei: meedoen aan een gesprek en een bericht. Dan
     127  // mag allebei hem tegenhouden. Zou alleen de messages-poort gelden, dan is
     128  // dat het gat waar een dichte replies-poort omheen loopt.
     129  db.prepare("UPDATE sites SET gate_replies = 0, gate_messages = 1 WHERE slug = 'kind'").run();
     130  const dm = { type: 'Create', object: { type: 'Note', content: '<p>psst</p>', inReplyTo: 'https://elders/x', to: ['https://elders/ap/users/vreemde'] } };
     131  const uit = await AP.ingestOutboxActivity(site(), user, dm);
     132  assert.equal(uit.error, 'gated_replies');
     133  db.prepare("UPDATE sites SET gate_replies = NULL, gate_messages = NULL WHERE slug = 'kind'").run();
     134});
     135
     136test('replies dicht laat de REDDINGSBOEI door, ook als die een antwoord is', async () => {
     137  // Het gevaarlijkste dat deze poort kan doen. Een kind dat om hulp vraagt in
     138  // een draadje waar het misgaat, is precies het geval waarvoor de boei bestaat.
     139  db.prepare("UPDATE sites SET gate_replies = 0 WHERE slug = 'kind'").run();
     140  const hulp = { type: 'Create', object: { type: 'Note', content: '<p>🛟</p>', 'shaer:helpRequest': true, inReplyTo: 'https://elders/x', to: ['https://elders/ap/users/oma'] } };
     141  const uit = await AP.ingestOutboxActivity(site(), user, hulp);
     142  assert.notEqual(uit.error, 'gated_replies');
     143  db.prepare("UPDATE sites SET gate_replies = NULL WHERE slug = 'kind'").run();
     144});
     145
    86146test('messages dicht weigert een direct bericht, maar NOOIT de reddingsboei', async () => {
    87147  db.prepare("UPDATE sites SET gate_messages = 0 WHERE slug = 'kind'").run();
     
    104164  db.prepare("UPDATE sites SET gate_account_move = NULL WHERE slug = 'kind'").run();
    105165});
     166
     167test('replies dicht houdt OOK het webpad tegen, niet alleen de app', async () => {
     168  // routes/posts.js roept deliverReply rechtstreeks aan en gaat nooit langs
     169  // ingestOutboxActivity. Een poort die alleen in C2S staat heeft een deur
     170  // ernaast -- en die deur is de eigen webinterface van het kind.
     171  db.prepare("UPDATE sites SET gate_replies = 0 WHERE slug = 'kind'").run();
     172  const parent = { id: 'https://elders/x', attributedTo: 'https://elders/ap/users/vreemde' };
     173  const uit = await AP.deliverReply(site(), { postId: '', postSlug: null, parent, text: 'ja!' });
     174  assert.equal(uit, null);
     175  db.prepare("UPDATE sites SET gate_replies = NULL WHERE slug = 'kind'").run();
     176});
Note: See TracChangeset for help on using the changeset viewer.