Files
Sylvain Duchesne f4050b95c0 test(e2e): éprouvé contre un vrai broker, et ce qu'on y apprend
69 tests unitaires, trois rounds d'auto-critique, et rien n'avait jamais tourné
contre un vrai broker. Sept parcours, vingt vérifications, à travers
ng-e2e-helpers — rien de réimplémenté.

La forme d'écriture est ACCEPTÉE par oxigraph, établie en relisant le document
et non parce que la mise à jour n'a pas levé : après curation, readUnion rend
deux sujets, celui de l'index et celui de l'objet de Bob, l'entrée portant sa
valeur. C'était l'une des deux inconnues.

L'autre est REPRODUITE, et c'est un défaut : un openInbox qui échoue en cours de
createIndex laisse un document orphelin. L'appel rejette et ne rend rien, mais
le document existe dans le store public du propriétaire, porte le descripteur,
et refuse les dépôts. Le paquet n'expose pas openInbox, donc il ne peut ni le
réparer ni le supprimer — orphelin permanent. Injecté pour être atteint : rien
de ce que contrôle un appelant ne fait échouer un vrai openDocumentInbox.

Et quatre endroits où la suite unitaire prouve moins qu'elle ne l'annonce, tous
vérifiés. Le plus net : les treize tests d'adaptateur ne chargent JAMAIS le vrai
polyfill. Preuve dure — la copie installée avait perdu un fichier qu'importe
surface/inbox.ts, et 69 sur 69 passaient quand même. Aucun des deux côtés n'a
tort ; c'est l'affirmation « l'adaptateur fonctionne » qui n'était pas testée.
Rien n'a été affaibli, l'e2e est ce qui la teste enfin.

Les trois autres sont de la même nature — une doublure trop faible plutôt qu'un
code faux : ses NURI n'ont pas la forme réelle, elle n'écrit pas la machinerie
que le vrai document porte, et étant une seule Map elle ne peut par construction
jamais révéler un retard de cohérence.

Trois exécutions, 27/27 chacune, autour de 58 secondes, aucune reprise.
2026-08-17 10:02:13 +02:00

87 lines
3.3 KiB
TypeScript

/**
* What is SPECIFIC to this repository in the end-to-end setup: the page that carries the
* application, and the name of the wallet its runs mint.
*
* Everything generic — the wallet lifecycle, the broker crossing, per-run profiles,
* bounds, the report shape, the recognition of the known browser failure modes — lives in
* `ng-e2e-helpers` and is used, never reimplemented. That package knows nothing about
* this one and must keep knowing nothing about it: it talks about NextGraph itself, so it
* outlives both the polyfill and this indexing layer.
*/
import { execSync } from "node:child_process";
import * as fs from "node:fs";
import * as path from "node:path";
import { fileURLToPath } from "node:url";
import {
mintWalletProfile,
serveOnEphemeralPort,
type RunProfile,
type WalletCredentials,
} from "ng-e2e-helpers";
const here = path.dirname(fileURLToPath(import.meta.url));
/**
* The throwaway credentials each run mints its own wallet with.
*
* A NAME, not an identity that survives: every run gets a profile of its own and mints
* this wallet into it, so two runs sharing the name share nothing else — which is what
* lets this suite run beside another repository's at the same time, against the same
* broker, without a lock. The password sits here in the clear because it opens a wallet
* that exists for the length of one run and is deleted with the profile holding it.
*/
export const WALLET: WalletCredentials = {
name: "ng-helpers-e2e",
password: "ng-helpers-e2e",
};
/** This run's physical user, in a profile of its own. */
export function mintRunWallet(suite: string): Promise<RunProfile> {
return mintWalletProfile(suite, WALLET);
}
/** `bun build` is a local bundle; a minute is already many times what it takes. */
const BUILD_MS = 60_000;
const ENTRY = path.resolve(here, "indexing-app.ts");
const BUNDLE_OUT = path.resolve(here, ".dist", "indexing-app.js");
export function buildApp(): void {
fs.mkdirSync(path.dirname(BUNDLE_OUT), { recursive: true });
execSync(`bun build ${ENTRY} --outfile ${BUNDLE_OUT} --bundle --format=esm`, {
stdio: "pipe",
cwd: path.resolve(here, ".."),
timeout: BUILD_MS,
});
}
/**
* Serve the application the way a deployment would.
*
* An unknown path 404s rather than answering with the page: a catch-all makes a request
* for a file nobody serves look like a perfectly good download, and hides exactly the
* kind of mistake a served asset can carry.
*/
export function serveApp(): Promise<{ url: string; close: () => void }> {
const bundle = fs.readFileSync(BUNDLE_OUT, "utf-8");
const html =
`<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">` +
`<title>ng-helpers indexing — e2e</title></head><body>` +
`<script type="module" src="/indexing-app.js"></script></body></html>`;
return serveOnEphemeralPort((req, res) => {
const route = (req.url ?? "/").split("?")[0];
if (route === "/indexing-app.js") {
res.writeHead(200, { "Content-Type": "application/javascript; charset=utf-8" });
res.end(bundle);
} else if (route === "/" || route === "/index.html") {
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
res.end(html);
} else {
res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
res.end("not served");
}
});
}