source: Klonkt/test/guardian-follow-queues.test.js@ 3a0ca0f

main
Last change on this file since 3a0ca0f was 3a0ca0f, checked in by roboburr <roboburr@…>, 4 weeks ago

De volgpoort zat alleen in de app, en volgverzoeken beslissen bij meerderheid

Barts twee meldingen (8-8), en de eerste is een echte vondst.

  1. WAAROM ESMEE'S VOLGVERZOEKEN NOOIT AANKWAMEN

Niet wat Bart vreesde -- het was niet zo dat alleen een intern account ze kon
beantwoorden. Die weg werkt: gateOutgoingFollow levert een Offer af bij een
guardian elders, en dat is precies wat dev als guardian van @mee had moeten
krijgen.

Er kwam alleen nooit iets om af te leveren. De poort stond in case 'Follow' van
de C2S-outbox, dus alleen als je via Shaer volgt. Volgde het kind vanuit Klonkts
eigen webinterface, dan werd er geen verzoek aangemaakt, ging er niets naar de
guardians, en was er dus ook niets te beantwoorden. Op dev staat geen enkele rij
in ap_follow_reviews en nergens een follow-approval-regel in de log.

Exact dezelfde deur-naast-de-poort als bij de antwoordpoort vanmiddag: de gate in
C2S, het webpad eromheen. De controle staat nu in followActor zelf, NA het
oplossen van de handle -- zou hij alleen naar de ruwe invoer kijken, dan is elke
@naam@server een sluiproute. approved is de enige doorlaat, voor
performApprovedFollow; zonder dat stuit een goedgekeurd verzoek opnieuw op de
poort en wacht het voor eeuwig.

Drie webroutes zeiden "Je volgt nu X" terwijl het verzoek bij de guardians lag.
Dat is de leugen die de poort waardeloos maakt: het kind denkt dat het gebeurd
is. Er is nu een derde uitkomst.

DE MUTATIE GAF EERST NUL FOUTEN -- er stond niets op deze poort. Nu vier toetsen,
inclusief het handle-geval en het goedgekeurde pad.

  1. VOLGVERZOEKEN BIJ EENVOUDIGE MEERDERHEID

Barts besluit: 1 van 2 is voldoende. followThreshold = ceil(n/2), voor beide
richtingen, want "mag dit kind met deze persoon te maken hebben" is dezelfde
vraag en twee drempels zou een guardian nooit kunnen uitleggen.

BEWUST SOEPELER DAN DE POORTDREMPEL (2 van 2): een gate opent een deur voor alles
wat daarna komt, een volgverzoek gaat over een persoon en is met ontvolgen terug
te draaien. En het is geen versoepeling maar een AANSCHERPING -- dit stond op
'any', een enkele ja hoeveel guardians er ook waren.

De 'all'-stand is weg: hij werd nergens gezet, dus was het een keuze die niemand
kon maken. De toets die hem bewaakte is herschreven, niet verwijderd.

Race naar de drempel zoals de poorttelling, met dezelfde TODO erbij (shaer-8vt):
wie antwoordt weet niet dat hij de doorslag geeft.

Suite 742/742; beide mutaties rood.

  • Property mode set to 100644
File size: 5.5 KB
Line 
1// Guardianship Fase 2 (shaer-jdb): gate-verzoeken bereiken een C2S-client.
2//
3// De gating zelf werkt sinds shaer-hxg. Wat ontbrak was het DOORGEVEN: de
4// wachtrij `follows` gaf hardgecodeerd een lege lijst terug ("not built in
5// Klonkt yet (Fase 2)") en `outgoing-follows` serveerde alleen de ward-kant.
6// Daardoor bleven beide secties in Shaer altijd leeg.
7//
8// En daaronder zat de echte fout: shaer:direction werd bij het versturen gezet
9// en nergens gelezen, dus een UITGAAND verzoek werd opgeslagen als "deze ward
10// wil deze ward volgen" met het doel weggegooid.
11//
12// Run: npm test
13
14import { test } from 'node:test';
15import assert from 'node:assert/strict';
16
17process.env.DATABASE_PATH = ':memory:';
18process.env.PUBLIC_BASE_URL = 'https://klonkt.test';
19
20const dbMod = await import('../src/config/database.js');
21const db = dbMod.default;
22dbMod.initializeDatabase();
23const G = await import('../src/services/guardianship/index.js');
24
25db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
26 .run('u1', 'oma', 'o@test', 'x', 'god');
27db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run('s1', 'oma', 'Oma', 'u1');
28
29const WARD = 'https://kind.test/ap/users/kind';
30const VREEMDE = 'https://elders.test/users/vreemde';
31const DOEL = 'https://muziek.test/users/band';
32const ME = 'https://klonkt.test/ap/users/oma';
33
34test('een INKOMEND verzoek komt in de follows-wachtrij', () => {
35 G.follows.recordReview('oma', {
36 id: 'f-in', wardUri: WARD, follower: VREEMDE, followerHandle: '@vreemde@elders.test',
37 direction: 'incoming',
38 });
39 const c = G.followsCollection(`${ME}/queues/follows`, 'oma', ME);
40 assert.equal(c.totalItems, 1, 'deze wachtrij gaf hiervoor altijd een lege lijst terug');
41 const it = c.orderedItems[0];
42 assert.equal(it.actor, VREEMDE, 'de vreemde is de volger');
43 assert.equal(it.object, WARD, 'en de ward is het doel');
44 assert.equal(it['shaer:direction'], 'incoming');
45});
46
47test('een UITGAAND verzoek komt in de outgoing-wachtrij, met het doel erin', () => {
48 // De kern van de bug: hiervoor werd dit als inkomend opgeslagen met ward ==
49 // follower, en het doel -- waar het antwoord over gaat -- viel weg.
50 G.follows.recordReview('oma', {
51 id: 'f-uit', wardUri: WARD, follower: WARD, direction: 'outgoing',
52 target: DOEL, targetHandle: '@band@muziek.test',
53 });
54 const c = G.outgoingFollowsCollection(`${ME}/queues/outgoing-follows`, 'oma', ME);
55 const it = c.orderedItems.find((x) => x.id === 'f-uit');
56 assert.ok(it, 'een guardian zag hier nooit iets: de wachtrij serveerde alleen de ward-kant');
57 assert.equal(it.actor, WARD, 'de ward is hier de volger');
58 assert.equal(it.object, DOEL);
59 assert.equal(it['shaer:target'], DOEL, 'zonder dit valt er niets te beoordelen');
60 assert.equal(it['shaer:targetHandle'], '@band@muziek.test');
61 assert.equal(it['shaer:direction'], 'outgoing');
62});
63
64test('de twee richtingen lopen niet door elkaar', () => {
65 const inn = G.followsCollection(`${ME}/q/f`, 'oma', ME).orderedItems.map((x) => x.id);
66 const uit = G.outgoingFollowsCollection(`${ME}/q/o`, 'oma', ME).orderedItems.map((x) => x.id);
67 assert.deepEqual(inn, ['f-in']);
68 assert.ok(uit.includes('f-uit') && !uit.includes('f-in'));
69});
70
71test('bij een remote ward wordt het aantal guardians WEGGELATEN, niet op nul gezet', () => {
72 // De guardian-set van een remote ward wordt op diens eigen server bijgehouden.
73 // Nul sturen zou lezen als "dit kind heeft geen guardians", en dat is het
74 // tegenovergestelde van onbekend.
75 const it = G.followsCollection(`${ME}/q/f`, 'oma', ME).orderedItems[0];
76 assert.equal(it['shaer:guardianCount'], undefined);
77});
78
79test('een oude rij zonder richting telt als inkomend', () => {
80 // Bestaande rijen missen de kolom en zijn niet te repareren -- de informatie
81 // stond er nooit in. Ze horen terug te vallen op het geval dat ze toen waren.
82 db.prepare(`INSERT INTO ap_follow_reviews (id, guardian_slug, ward_uri, follower_uri, direction)
83 VALUES ('f-oud','oma',?,?,NULL)`).run(WARD, VREEMDE);
84 const inn = G.followsCollection(`${ME}/q/f`, 'oma', ME).orderedItems.map((x) => x.id);
85 assert.ok(inn.includes('f-oud'));
86});
87
88// ── De drempel voor een volgverzoek (Barts besluit, 8-8) ────────────────
89
90test('een eenvoudige meerderheid: 1 van 2 is voldoende', () => {
91 // Barts woorden. Bewust soepeler dan de POORTdrempel: een gate opent een deur
92 // voor alles wat daarna komt, een volgverzoek gaat over een persoon en is met
93 // ontvolgen terug te draaien.
94 assert.equal(G.follows.followThreshold(1), 1);
95 assert.equal(G.follows.followThreshold(2), 1);
96 assert.equal(G.follows.followThreshold(3), 2);
97 assert.equal(G.follows.followThreshold(4), 2);
98});
99
100test('en dat is een AANSCHERPING, geen versoepeling', () => {
101 // Tot vandaag stond dit op 'any': een enkele ja, hoeveel guardians er ook
102 // waren. Bij drie of meer is er nu meer nodig, niet minder.
103 assert.ok(G.follows.followThreshold(3) > 1);
104 assert.ok(G.follows.followThreshold(5) > 1);
105});
106
107test('nul guardians vraagt nog steeds iemand', () => {
108 // Een lege set mag nooit "iedereen is het eens" opleveren. Dat is de stille
109 // fout waarmee een verzoek zichzelf goedkeurt.
110 assert.equal(G.follows.followThreshold(0), 1);
111});
112
113test('de drempel voor een POORT blijft strikter', () => {
114 // Twee verschillende vragen, twee drempels, en dat verschil is opzet: 2 van 2
115 // voor een gate, 1 van 2 voor een volgverzoek.
116 assert.equal(G.gated.thresholdFor(2), 2);
117 assert.equal(G.follows.followThreshold(2), 1);
118});
Note: See TracBrowser for help on using the repository browser.