test(e2e): un user physique par batterie — 20 min et 286s de synchro tombent à 3,6 min et 30s
La suite se ralentissait elle-même, de façon monotone. Chaque batterie crée ~11 identités virtuelles FRAÎCHES (`@alice-…`, `@owner-…`, `@recon-…`), chacune avec ses trois documents de scope et son inbox, et toutes atterrissaient dans le MÊME user physique — un wallet créé le 10 juillet et réutilisé depuis, que rien ne nettoyait. Or une resynchronisation à froid est O(taille du user physique), ce que la doc de cette bibliothèque énonce elle-même. D'où 250s il y a une semaine, 286s avant-hier, et une batterie qui a fini par dépasser les 20 minutes. Les identités fraîches ne sont pas la faute : ce sont elles qui rendent une batterie reproductible, une inbox stable accumulant sinon les dépôts des runs précédents. La faute était de conserver le user physique qui les héberge. Mesuré : 42/42 en **3,6 min** au lieu de 20+, synchro à froid la plus lente à **30s** au lieu de 286s. Le profil reste persistant À L'INTÉRIEUR d'une batterie — CONTRACT 1 et 2 testent précisément cela (reconnexion fidèle sur le même profil, absence de fork de compte au travers). Deux garde-fous pour que la prochaine dérive se voie : - **Le budget appartient au runner**, qui échoue en nommant la cause probable. Un `timeout` posé autour de la commande tuait le navigateur, et la suite rapportait « Target page, context or browser has been closed » — un message qui se lit comme un défaut applicatif, et que j'ai diagnostiqué deux fois de travers avant de comparer les durées. - **La synchro à froid remonte dans le résumé.** C'est le nombre qui a dérivé pendant un mois sans que personne le regarde, parce qu'il n'apparaissait qu'au détour de la ligne de détail d'une étape.
This commit is contained in:
@@ -91,6 +91,37 @@ async function faithfulReconnect(
|
||||
return { page: p, frame };
|
||||
}
|
||||
|
||||
/**
|
||||
* The batch's own budget, and the measurement that explains an overrun.
|
||||
*
|
||||
* Not a `timeout` wrapped around the command from outside: when that fired it killed the
|
||||
* browser, and the suite reported `Target page, context or browser has been closed` —
|
||||
* which reads as an application bug and was twice diagnosed as one. A budget belongs to
|
||||
* the thing that knows what it is spending it on, and it must say so when it runs out.
|
||||
*/
|
||||
const BATCH_BUDGET_MS = 45 * 60 * 1000;
|
||||
const batchStart = Date.now();
|
||||
/**
|
||||
* The slowest cold resynchronisation of the batch — the number that drifted from 250s to
|
||||
* 286s over a month without anyone looking, because it only ever appeared inside one
|
||||
* step's detail line. It is the health indicator of the physical user, so it is reported
|
||||
* with the summary.
|
||||
*/
|
||||
let coldSyncMs = 0;
|
||||
|
||||
/** Fail with the cause named, rather than letting a killed browser look like a defect. */
|
||||
function assertWithinBudget(): void {
|
||||
const spent = Date.now() - batchStart;
|
||||
if (spent > BATCH_BUDGET_MS) {
|
||||
throw new Error(
|
||||
`[e2e] batch budget exceeded (${Math.round(spent / 60000)} min > ` +
|
||||
`${BATCH_BUDGET_MS / 60000} min). This is almost always the physical user having ` +
|
||||
"grown: a cold resync is O(its size). Check the cold-sync figure printed above — " +
|
||||
"it should be stable from batch to batch now that each gets a fresh wallet.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
console.log("[e2e] building SDK page bundle...");
|
||||
buildBundle();
|
||||
@@ -522,7 +553,8 @@ async function main(): Promise<void> {
|
||||
`[SYNC] reconnect re-reads its OWN persisted ${scope} marker (cold-read)`,
|
||||
found,
|
||||
found
|
||||
? `synced in ${syncMs}ms (reconnect-login ${loginMs}ms) — ${lastDetail}`
|
||||
? ((coldSyncMs = Math.max(coldSyncMs, syncMs)),
|
||||
`synced in ${syncMs}ms (reconnect-login ${loginMs}ms) — ${lastDetail}`)
|
||||
: `NEVER synced within 120s (reconnect-login ${loginMs}ms) — ${lastDetail}`,
|
||||
);
|
||||
}
|
||||
@@ -744,7 +776,14 @@ async function main(): Promise<void> {
|
||||
// ── Summary ───────────────────────────────────────────────────────────────
|
||||
const passed = results.filter((r) => r.ok).length;
|
||||
const failed = results.length - passed;
|
||||
console.log(`\n══ SDK e2e summary: ${passed} passed, ${failed} failed, ${results.length} total ══`);
|
||||
const batchMin = ((Date.now() - batchStart) / 60000).toFixed(1);
|
||||
console.log(
|
||||
`\n══ SDK e2e summary: ${passed} passed, ${failed} failed, ${results.length} total ` +
|
||||
`— batch ${batchMin} min, slowest cold sync ${Math.round(coldSyncMs / 1000)}s ══`,
|
||||
);
|
||||
// A fresh wallet per batch is what should keep the cold sync flat; if it climbs from
|
||||
// one batch to the next, the per-batch wallet is not being discarded.
|
||||
assertWithinBudget();
|
||||
if (failed > 0) {
|
||||
console.log("Failures:");
|
||||
for (const r of results.filter((x) => !x.ok)) console.log(` - ${r.name}: ${r.detail ?? ""}`);
|
||||
|
||||
Reference in New Issue
Block a user