Files
UmbrelApps/tests/test_client_lokaal.mjs
T

146 lines
6.1 KiB
JavaScript
Raw Normal View History

// ═══════════════════════════════════════════════════════════════════════════════
// 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);