source: Klonkt/test/guardian-gate-panel.test.js@ 75f2897

main
Last change on this file since 75f2897 was f3a2556, checked in by roboburr <roboburr@…>, 5 weeks ago

De apps kunnen nu zien wat er voor een ward gated is (shaer-ahy.1)

Bart: iOS en Android op hetzelfde plan als de PWA. Dit is het blokkerende stuk --
zonder dit kan geen app een poort tonen.

wardsCollection gaf {id, handle, since}. Een app kon een ward dus wel TONEN maar
niets over hem zeggen, en dat is precies de helft van het antwoord op "wat mag
dit kind". Er zit nu shaer:gates bij: dezelfde rijen als het PWA-paneel, met
soort, drempel, lopend voorstel en omkeerbaarheid.

UIT DEZELFDE FUNCTIE, en dat is de kern. wardGates stond in routes/guardian.js
en daar kon alleen de PWA erbij. Een tweede berekening ernaast zou vroeg of laat
een ander antwoord geven op dezelfde vraag -- geen schoonheidsfoutje, maar twee
guardians die een verschillend beeld van hetzelfde kind krijgen. Verplaatst naar
queues.js; beide schermen lezen er nu uit, en een toets vergelijkt de twee
uitkomsten letterlijk met deepEqual.

Suite 688/688; zonder de nieuwe regel vallen er twee om.

  • Property mode set to 100644
File size: 8.7 KB
RevLine 
[792af53]1// Het gate-paneel per ward (shaer-ahy.1).
2//
3// Wat gated wordt is een ontwerpkeuze van de implementatie -- de FEP levert het
4// mechanisme en een paar voorbeelden, niet de lijst. GATE_CATALOGUE is die lijst
5// op een plek, en gateRows maakt er de rijen van die het paneel toont.
6//
7// De regels die hier bewaakt worden zijn geen opmaak maar betekenis: een gate
8// die je niet kunt verzetten hoort tOch zichtbaar te zijn, onbekend is niet uit,
9// en er wordt geen drempel verzonnen die we niet kennen.
10//
11// Run: npm test
12
13import { test } from 'node:test';
14import assert from 'node:assert/strict';
15
16process.env.DATABASE_PATH = ':memory:';
17const gated = await import('../src/services/guardianship/gated.js');
18
19const rij = (rows, f) => rows.find((r) => r.feature === f);
20
21test('elke gate uit de catalogus krijgt een rij, ook de vaste', () => {
22 // Een paneel dat alleen verstelbare dingen toont verzwijgt de helft van wat er
23 // voor dit kind geldt.
24 const rows = gated.gateRows({});
25 assert.equal(rows.length, gated.GATE_CATALOGUE.length);
26 assert.ok(rij(rows, 'shaer:follows'), 'volgverzoeken zijn altijd gated en horen er tOch te staan');
27 assert.equal(rij(rows, 'shaer:follows').adjustable, false, 'maar niet te verzetten');
28});
29
30test('het soort staat erbij, want ze werken niet hetzelfde', () => {
31 const rows = gated.gateRows({});
32 assert.equal(rij(rows, 'shaer:externalEmbeds').kind, 'setting');
33 assert.equal(rij(rows, 'shaer:follows').kind, 'perRequest');
34});
35
36test('de drempel volgt het aantal guardians', () => {
37 const drie = gated.gateRows({ guardianCount: 3 });
38 assert.deepEqual(rij(drie, 'shaer:externalEmbeds').threshold, { need: 2, of: 3 });
39 const een = gated.gateRows({ guardianCount: 1 });
40 assert.deepEqual(rij(een, 'shaer:externalEmbeds').threshold, { need: 1, of: 1 });
41});
42
43test('een ONBEKEND aantal guardians levert GEEN drempel op', () => {
44 // Bij een ward elders wordt die set op diens eigen server bijgehouden. Een
45 // verzonnen getal leest als een feit, en dit is precies waar een guardian op
46 // afgaat voordat hij iets voorstelt.
47 const rows = gated.gateRows({ guardianCount: null });
48 assert.equal(rij(rows, 'shaer:externalEmbeds').threshold, null);
49});
50
51test('afspelen is dicht zolang zien UIT staat', () => {
52 const rows = gated.gateRows({ settings: { 'shaer:externalEmbeds': false } });
53 const p = rij(rows, 'shaer:externalPlayback');
54 assert.equal(p.adjustable, false);
55 assert.equal(p.blockedBy, 'shaer:externalEmbeds');
56});
57
58test('maar ONBEKEND is niet hetzelfde als uit', () => {
59 // Verbergen bij onbekend betekende ooit dat een guardian elders afspelen nooit
60 // kon voorstellen -- daar ging een hele voorstelronde de verkeerde gate in.
61 const rows = gated.gateRows({ settings: { 'shaer:externalEmbeds': null } });
62 const p = rij(rows, 'shaer:externalPlayback');
63 assert.equal(p.adjustable, true);
64 assert.equal(p.blockedBy, undefined);
65});
66
67test('een lopend voorstel hangt aan zijn eigen gate', () => {
68 const rows = gated.gateRows({
69 proposals: [{ feature: 'shaer:externalPlayback', value: true, status: 'open' }],
70 });
71 assert.equal(rij(rows, 'shaer:externalPlayback').proposal.status, 'open');
72 assert.equal(rij(rows, 'shaer:externalEmbeds').proposal, undefined);
73});
74
75test('wat er wacht staat bij de gate waar het op wacht', () => {
76 const rows = gated.gateRows({ waiting: { 'shaer:follows': 2 } });
77 assert.equal(rij(rows, 'shaer:follows').waiting, 2);
78});
79
[72d8eff]80test('de stand komt uit de BESLUITEN, en zegt of er wel besloten is', () => {
81 // Er zijn geen lokale accounts: elke ward woont elders, dus de kolom op onze
82 // eigen sites-tabel is voor iedere ward leeg. Wat een guardian wel heeft is de
83 // uitslag van wat hij voorstelde.
84 const aan = gated.gateRows({ settings: { 'shaer:externalEmbeds': { value: true, decided: true } } });
85 assert.equal(rij(aan, 'shaer:externalEmbeds').value, true);
86 assert.equal(rij(aan, 'shaer:externalEmbeds').decided, true);
87});
88
89test('nooit besloten is iets anders dan besloten-uit', () => {
90 // "Uit" en "voor zover wij weten uit" zijn niet hetzelfde. Dat tweede als een
91 // besluit tonen suggereert dat iemand het genomen heeft.
92 const r = rij(gated.gateRows({ settings: { 'shaer:externalEmbeds': { value: false, decided: false } } }), 'shaer:externalEmbeds');
93 assert.equal(r.value, false);
94 assert.equal(r.decided, false);
95});
96
97test('de trap blokkeert alleen op een BESLOTEN dicht', () => {
98 // Blokkeren op de standaard zou betekenen dat afspelen nooit als eerste
99 // voorgesteld kan worden -- en zo ging er ooit een hele voorstelronde de
100 // verkeerde gate in.
101 const standaard = gated.gateRows({ settings: { 'shaer:externalEmbeds': { value: false, decided: false } } });
102 assert.equal(rij(standaard, 'shaer:externalPlayback').adjustable, true);
103 const beslist = gated.gateRows({ settings: { 'shaer:externalEmbeds': { value: false, decided: true } } });
104 assert.equal(rij(beslist, 'shaer:externalPlayback').adjustable, false);
105});
106
107test('de oude vorm (kale boolean) blijft werken', () => {
108 // Backwards: een aanroeper die nog true/false doorgeeft hoort niet om te vallen.
109 const r = rij(gated.gateRows({ settings: { 'shaer:externalEmbeds': true } }), 'shaer:externalEmbeds');
110 assert.equal(r.value, true);
111 assert.equal(r.decided, true);
112});
113
[792af53]114test('omkeerbaarheid staat per gate genoteerd', () => {
115 // Nu is alles omkeerbaar. Zodra independence erbij komt (shaer-90v) is dat het
116 // niet, en dan moet het paneel dat kunnen zeggen zonder verbouwing.
117 for (const r of gated.gateRows({})) assert.equal(typeof r.reversible, 'boolean');
118});
[b2646cc]119
120// ── De waarschuwing bij een voorstel (shaer-nf9) ────────────────────────
121
122test('een gewone gate waarschuwt omkeerbaar, niet onomkeerbaar', () => {
123 // Barts zin "een geopende poort gaat niet meer dicht" klopt niet over de
124 // INSTELLING -- die gaat wel weer dicht. Zo'n aantoonbaar onware waarschuwing
125 // neemt de rest van het scherm mee in zijn val zodra iemand het merkt.
126 assert.equal(gated.gateConsequence('shaer:externalEmbeds'), 'reversible');
127});
128
129test('independence waarschuwt WEL onomkeerbaar', () => {
130 // De enige die gezag overdraagt (shaer-90v). Daar is Barts zin wel waar.
131 assert.equal(gated.gateConsequence('shaer:independence'), 'irreversible');
132});
133
134test('een gate die we NIET kennen krijgt de zwaarste tekst', () => {
135 // Een mede-guardian elders kan iets voorstellen dat onze catalogus niet kent.
136 // Bij twijfel waarschuwen we zwaarder, niet lichter: de faalstand die hier pijn
137 // doet is een guardian die iets doorlaat omdat het scherm er licht over deed.
138 assert.equal(gated.gateConsequence('shaer:ietsNieuws'), 'unknown');
139});
140
141test('elke gate uit de catalogus levert een gevolg op', () => {
142 // Anders valt er stilletjes een rij zonder waarschuwing door.
143 for (const g of gated.GATE_CATALOGUE) {
144 assert.ok(['reversible', 'irreversible'].includes(gated.gateConsequence(g.feature)), g.feature);
145 }
146});
[f3a2556]147
148// ── Dezelfde poorten voor het paneel en voor de apps (shaer-ahy.1) ───────
149
150test('de wards-queue draagt de poorten mee', async () => {
151 // Zonder dit kon een app een ward wel TONEN maar niets over hem zeggen. De
152 // apps lezen deze collectie; het PWA-paneel leest wardGates rechtstreeks.
153 const q = await import('../src/services/guardianship/queues.js');
154 const rel = await import('../src/services/guardianship/relations.js');
155 const dbm = await import('../src/config/database.js');
156 dbm.initializeDatabase();
157 dbm.default.prepare('INSERT OR IGNORE INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
158 .run('gu', 'gu', 'gu@t', 'x', 'god');
159 dbm.default.prepare('INSERT OR IGNORE INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run('gs', 'oma', 'Oma', 'gu');
160 rel.commitWardForGuardian('oma', 'https://elders/ap/users/kind', { handle: '@kind' });
161
162 const coll = q.wardsCollection('https://x/queues/wards', 'oma');
163 const ward = coll.orderedItems.find((w) => w.id === 'https://elders/ap/users/kind');
164 assert.ok(ward, 'de ward staat in de collectie');
165 assert.ok(Array.isArray(ward['shaer:gates']), 'en draagt zijn poorten');
166 assert.equal(ward['shaer:gates'].length, gated.GATE_CATALOGUE.length);
167});
168
169test('de app krijgt PRECIES dezelfde rijen als het paneel', async () => {
170 // Twee berekeningen naast elkaar geven vroeg of laat een ander antwoord op
171 // dezelfde vraag, en dat is hier geen schoonheidsfoutje: dan krijgen twee
172 // guardians een verschillend beeld van hetzelfde kind.
173 const q = await import('../src/services/guardianship/queues.js');
174 const coll = q.wardsCollection('https://x/queues/wards', 'oma');
175 const uitQueue = coll.orderedItems.find((w) => w.id === 'https://elders/ap/users/kind')['shaer:gates'];
176 const uitPaneel = q.wardGates('oma', 'https://elders/ap/users/kind');
177 assert.deepEqual(uitQueue, uitPaneel);
178});
Note: See TracBrowser for help on using the repository browser.