source: Klonkt/test/gates-capabilities-parity.test.js@ 6c4ff7e

main
Last change on this file since 6c4ff7e was e215090, checked in by Robin <roboburr@…>, 4 weeks ago

Het verschil draagt ook de rechten

Zonder dit valt de client bij elke verschil-lezing terug op zijn standaard
Caps, en die standaard is 'alles mag': een gesloten poort zou zichzelf stil
openzetten. Dezelfde reden waarom een 304 de caps met rust laat -- dat stond
er al, met de reden erbij, en dat gold hier net zo goed.

Het rechtenblok staat nu in capabilitiesOf() zodat beide lezingen hetzelfde
beschrijven. De pariteitstest leest de bron en wees naar de oude plek; die
wijst nu naar de functie.

862/862 groen.

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

  • Property mode set to 100644
File size: 3.6 KB
Line 
1// Twee lijsten die hetzelfde horen te weten, en niets dat ze bij elkaar houdt.
2//
3// GATE_CATALOGUE zegt WAT er gated is. Het shaer:capabilities-blok zegt wat een
4// account op dit moment MAG. Ze worden allebei met de hand bijgehouden, op twee
5// plekken, en er komt geen fout van als er eentje achterblijft: de gate
6// verschijnt netjes in het paneel van de guardian, en de app van het kind hoort
7// er nooit van. Die stilte is het probleem -- de app ontdekt de poort dan pas
8// bij de eerste weigering, wat precies is wat het commentaar bij dat blok zegt
9// te willen voorkomen ("de app hoort VOORAF te weten wat hij mag aanbieden").
10//
11// Zo'n gat is echt gevallen: shaer:accountMove stond in de catalogus met een
12// kolom en al, en ontbrak in capabilities. Deze test is de reden dat het de
13// volgende keer opvalt zonder dat iemand het toevallig ziet.
14//
15// Eén richting, met opzet. Niet elke capability is een gate: shaer:externalLinks
16// hangt aan de playback-poort en shaer:cursor is helemaal geen poort maar een
17// leesteken voor de wachtrij.
18import { test } from 'node:test';
19import assert from 'node:assert/strict';
20import fs from 'fs';
21
22process.env.DATABASE_PATH = ':memory:';
23process.env.PUBLIC_BASE_URL = 'https://test.example';
24
25const dbMod = await import('../src/config/database.js');
26dbMod.initializeDatabase();
27const gated = await import('../src/services/guardianship/gated.js');
28
29const SOURCE = 'src/routes/activitypub.js';
30
31/** De sleutels van het rechtenblok, gelezen uit de bron.
32 *
33 * Het blok woont sinds 10-8 in capabilitiesOf(), omdat de verschil-lezing
34 * (?changes=1) dezelfde rechten moet dragen: een antwoord zonder rechten laat
35 * de client terugvallen op zijn standaard, en die standaard is 'alles mag'.
36 * Deze toets kijkt daarom naar die functie en niet meer naar de plek in de
37 * route. */
38function capabilityKeys() {
39 const src = fs.readFileSync(SOURCE, 'utf8');
40 const start = src.indexOf('function capabilitiesOf(');
41 assert.notEqual(start, -1, `${SOURCE} heeft geen capabilitiesOf() meer`);
42 const open = src.indexOf('{', start);
43 let depth = 0;
44 let end = open;
45 for (let i = open; i < src.length; i++) {
46 if (src[i] === '{') depth++;
47 else if (src[i] === '}') { depth--; if (depth === 0) { end = i; break; } }
48 }
49 const body = src.slice(open + 1, end);
50 return new Set([...body.matchAll(/'(shaer:[a-zA-Z]+)'\s*:/g)].map((m) => m[1]));
51}
52
53test('elke gate met een eigen kolom staat ook in shaer:capabilities', () => {
54 const caps = capabilityKeys();
55 assert.ok(caps.size > 5, 'het blok is gevonden en niet leeg');
56
57 // Een kolom hebben is het criterium: dat is precies de verzameling poorten
58 // waar een stand van bewaard wordt, en dus waar de app iets aan heeft. Een
59 // vaste poort (shaer:follows) heeft er geen, want er valt niets te bewaren,
60 // en een geplande (available: false) bestaat nog niet.
61 const missing = gated.GATE_CATALOGUE
62 .filter((g) => g.available !== false && gated.featureColumn(g.feature))
63 .map((g) => g.feature)
64 .filter((f) => !caps.has(f));
65
66 assert.deepEqual(missing, [],
67 `deze poorten bestaan wel maar de app krijgt ze niet te horen: ${missing.join(', ')}`);
68});
69
70test('een vaste of geplande poort hoeft er niet in te staan', () => {
71 // De regel hierboven mag niet zo streng worden dat hij dingen eist die geen
72 // stand hebben. shaer:follows ligt vast (§5.3) en shaer:publicProfile bestaat
73 // nog niet; allebei terecht afwezig, en dat moet zo kunnen blijven.
74 const caps = capabilityKeys();
75 assert.equal(caps.has('shaer:follows'), false, 'vast: geen kolom, geen capability');
76 assert.equal(gated.featureColumn('shaer:publicProfile'), null, 'gepland: nog geen kolom');
77});
Note: See TracBrowser for help on using the repository browser.