146 lines
6.1 KiB
JavaScript
146 lines
6.1 KiB
JavaScript
// ═══════════════════════════════════════════════════════════════════════════════
|
|||
|
|
// Toetst de testcliënt zonder relay: tools/relay-client/src/client.js
|
||
|
|
//
|
||
|
|
// Draaien: node tests/test_client_lokaal.mjs
|
||
|
|
//
|
||
|
|
// Anders dan de rest van deze map heeft deze toets wél afhankelijkheden nodig,
|
||
|
|
// namelijk die van tools/relay-client. Staat `node_modules` daar niet, dan slaat
|
||
|
|
// hij zichzelf over en meldt dat, zoals de certificaattoetsen doen als de
|
||
|
|
// certificaatwinkel van het besturingssysteem ontbreekt. Installeren gaat met
|
||
|
|
// `npm install` in tools/relay-client.
|
||
|
|
//
|
||
|
|
// Waarom dit bestand er is: `@evolu/nodejs` bevat geen cliënt, dus de
|
||
|
|
// afhankelijkheden voor `createEvolu` worden in src/evolu-node.js zelf
|
||
|
|
// samengesteld. Dat is de plek die bij een versieverhoging van Evolu als eerste
|
||
|
|
// omvalt, en hij faalt stil: alle vier de fouten die dat bestand tijdens de bouw
|
||
|
|
// opleverde gaven een instantie die het lijkt te doen, tot de eerste
|
||
|
|
// schrijfactie. **Deze toets doet dus een schrijfactie**, en aanmaken alleen is
|
||
|
|
// geen bewijs.
|
||
|
|
//
|
||
|
|
// Geen relay: `openStore` zonder `relayUrl`. Wat hier getoetst wordt is de
|
||
|
|
// cliënt, en die is los van het netwerk te beproeven. Het toegangsbeleid van de
|
||
|
|
// relay staat in test_limiter.mjs.
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
import { mkdtempSync, rmSync } from 'node:fs';
|
||
|
|
import { tmpdir } from 'node:os';
|
||
|
|
import { join } from 'node:path';
|
||
|
|
|
||
|
|
// Een wachthond, en die is hier niet voor de sier. Gaat er iets mis in de
|
||
|
|
// samenstelling, dan komt dat er meestal uit als een schrijfactie die nooit
|
||
|
|
// antwoordt in plaats van een fout: het defect zit in een worker en de
|
||
|
|
// `loadQuery` erna blijft hangen. Zonder deze wachthond blijft de suite dus
|
||
|
|
// staan in plaats van rood te worden, en dat is bij de mutatietoets van
|
||
|
|
// 09-09-2026 letterlijk gebeurd.
|
||
|
|
const WACHTHOND_SECONDEN = 30;
|
||
|
|
setTimeout(() => {
|
||
|
|
console.log(`FOUT: niets meer gehoord na ${WACHTHOND_SECONDEN} seconden.`);
|
||
|
|
console.log(' Dat wijst op een defect in een worker; zie tools/relay-client/src/evolu-node.js.');
|
||
|
|
console.log('');
|
||
|
|
console.log('afgebroken');
|
||
|
|
process.exit(1);
|
||
|
|
}, WACHTHOND_SECONDEN * 1000).unref();
|
||
|
|
|
||
|
|
let goed = 0;
|
||
|
|
let fout = 0;
|
||
|
|
|
||
|
|
const toets = (omschrijving, voorwaarde) => {
|
||
|
|
if (voorwaarde) {
|
||
|
|
goed += 1;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
fout += 1;
|
||
|
|
console.log(`FOUT: ${omschrijving}`);
|
||
|
|
};
|
||
|
|
|
||
|
|
// ── Zijn de afhankelijkheden er? ──────────────────────────────────────────────
|
||
|
|
|
||
|
|
let client;
|
||
|
|
try {
|
||
|
|
client = await import('../tools/relay-client/src/client.js');
|
||
|
|
} catch (oorzaak) {
|
||
|
|
console.log('OVERGESLAGEN: de pakketten van tools/relay-client ontbreken.');
|
||
|
|
console.log(` Draai daar eerst \`npm install\`. (${oorzaak.message})`);
|
||
|
|
console.log('');
|
||
|
|
console.log('0 goed, 0 fout');
|
||
|
|
process.exit(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
const { eigenaarUitMnemonic, nieuweEigenaar, openStore } = client;
|
||
|
|
|
||
|
|
// ── Een mnemonic levert altijd hetzelfde OwnerId op ───────────────────────────
|
||
|
|
|
||
|
|
{
|
||
|
|
// Dit is de toets die omvalt als Evolu onder ons iets anders gaat doen met de
|
||
|
|
// afleiding van een eigenaar. Vaste mnemonic, vast antwoord. Het is geen
|
||
|
|
// geheim: deze eigenaar bestaat alleen in deze toets, en het is bovendien de
|
||
|
|
// bekendste testmnemonic die er is.
|
||
|
|
const MNEMONIC =
|
||
|
|
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
|
||
|
|
|
||
|
|
const eerste = eigenaarUitMnemonic(MNEMONIC);
|
||
|
|
const tweede = eigenaarUitMnemonic(MNEMONIC);
|
||
|
|
|
||
|
|
toets('dezelfde mnemonic geeft hetzelfde OwnerId', eerste.id === tweede.id);
|
||
|
|
toets('en een OwnerId is een niet-lege string', typeof eerste.id === 'string' && eerste.id.length > 0);
|
||
|
|
toets('de eigenaar houdt zijn mnemonic bij zich', eerste.mnemonic === MNEMONIC);
|
||
|
|
toets('een verse eigenaar krijgt een ander OwnerId', nieuweEigenaar().id !== eerste.id);
|
||
|
|
|
||
|
|
let geweigerd = false;
|
||
|
|
try {
|
||
|
|
eigenaarUitMnemonic('dit is geen mnemonic');
|
||
|
|
} catch {
|
||
|
|
geweigerd = true;
|
||
|
|
}
|
||
|
|
// Het niet-gelukkige pad, en dat is hier het belangrijkste: stil een andere
|
||
|
|
// eigenaar maken zou een proef opleveren die nergens over gaat.
|
||
|
|
toets('een onleesbare mnemonic wordt geweigerd', geweigerd);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Schrijven en teruglezen, zonder relay ─────────────────────────────────────
|
||
|
|
|
||
|
|
const map = mkdtempSync(join(tmpdir(), 'relay-client-toets-'));
|
||
|
|
let store = null;
|
||
|
|
|
||
|
|
try {
|
||
|
|
const defecten = [];
|
||
|
|
store = await openStore({
|
||
|
|
directory: map,
|
||
|
|
owner: nieuweEigenaar(),
|
||
|
|
onDefect: (defect) => {
|
||
|
|
defecten.push(defect);
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
toets('zonder relayUrl is er geen transport', store.transportUrl === null);
|
||
|
|
|
||
|
|
const bytes = new Uint8Array([0, 1, 2, 253, 254, 255]);
|
||
|
|
const geschreven = store.schrijf('toets', bytes);
|
||
|
|
toets('een blob wegschrijven geeft een id terug', typeof geschreven?.id === 'string');
|
||
|
|
|
||
|
|
const rijen = await store.lees();
|
||
|
|
toets('en die blob komt er weer uit', rijen.length === 1);
|
||
|
|
toets('met hetzelfde label', rijen[0]?.label === 'toets');
|
||
|
|
toets(
|
||
|
|
'en met de bytes ongeschonden',
|
||
|
|
rijen[0]?.body?.length === bytes.length
|
||
|
|
&& [...(rijen[0]?.body ?? [])].every((waarde, plek) => waarde === bytes[plek]),
|
||
|
|
);
|
||
|
|
|
||
|
|
const sluiten = store.sluit;
|
||
|
|
store = null;
|
||
|
|
await sluiten();
|
||
|
|
|
||
|
|
// Afsluiten hoort stil te zijn. Ging dat mis, dan bleef er een worker draaien
|
||
|
|
// die zijn eigen run al kwijt was, en dat merk je pas als een proces blijft
|
||
|
|
// hangen. Dat was de vierde valstrik uit PLAN.md §4e.
|
||
|
|
toets(`afsluiten levert geen defect op (${defecten.length} gezien)`, defecten.length === 0);
|
||
|
|
} finally {
|
||
|
|
if (store) await store.sluit();
|
||
|
|
rmSync(map, { recursive: true, force: true });
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log('');
|
||
|
|
console.log(`${goed} goed, ${fout} fout`);
|
||
|
|
process.exit(fout === 0 ? 0 : 1);
|