Ng eventually #1

Open
Sylvain wants to merge 110 commits from ng-eventually into main
2 changed files with 50 additions and 12 deletions
Showing only changes of commit 0fc8479e22 - Show all commits
+33 -12
View File
@@ -89,6 +89,28 @@ function dirSizeMB(dir: string): number {
}
}
/**
* Remove any stale Chromium singleton files from `profilePath`. Chromium refuses
* to launch (ProcessSingleton error) if SingletonLock, SingletonCookie, or
* SingletonSocket are left over from a previous crashed run. Idempotent — safe to
* call even when the profile does not exist yet.
*/
function cleanSingletons(profilePath: string, label: string): void {
if (!fs.existsSync(profilePath)) return;
const singletons = ["SingletonLock", "SingletonCookie", "SingletonSocket"];
for (const name of singletons) {
const p = path.join(profilePath, name);
if (fs.existsSync(p)) {
try {
fs.rmSync(p, { force: true });
console.log(`[rotate] ${label}: removed stale ${name}.`);
} catch {
// Non-fatal: if we can't remove it, launch will fail with a clear error
}
}
}
}
function rotateProfile(profilePath: string, label: string): void {
const sizeMB = dirSizeMB(profilePath);
if (sizeMB > BLOAT_THRESHOLD_MB) {
@@ -102,17 +124,9 @@ function rotateProfile(profilePath: string, label: string): void {
console.warn(`[rotate] ${label}: failed to remove profile: ${e}`);
}
} else {
// Even if we keep the profile, remove any stale SingletonLock left by a
// previous crashed run — Chromium refuses to launch if the lock exists.
const lockPath = path.join(profilePath, "SingletonLock");
if (fs.existsSync(lockPath)) {
try {
fs.rmSync(lockPath, { force: true });
console.log(`[rotate] ${label}: removed stale SingletonLock.`);
} catch {
// Non-fatal: if we can't remove it, launch will fail with a clear error
}
}
// Even if we keep the profile, remove any stale Chromium singleton files left
// by a previous crashed run — Chromium refuses to launch if they exist.
cleanSingletons(profilePath, label);
console.log(
`[rotate] ${label}: ${sizeMB}MB — below threshold, keeping profile.`,
);
@@ -380,6 +394,10 @@ async function main(): Promise<void> {
);
// ── (b) Polyfill e2e real broker ──────────────────────────────────────────
// Clean singleton files immediately before launching Chromium — guards against
// any file left by polyfill:unit (unlikely but defensive) or by a previous
// interrupted run that the initial rotateProfile call ran before.
cleanSingletons(POLYFILL_PROFILE, "polyfill-lib (pre-e2e)");
steps.push(
runStep(
"polyfill:e2e",
@@ -416,11 +434,14 @@ async function main(): Promise<void> {
}
// ── (d) Festipod @multibrowser ────────────────────────────────────────────
// Exclude @wip: a scenario tagged @wip @multibrowser (e.g. the reactive
// cross-session scenario, blocked by the shared-wallet structural limit) must
// not gate the baseline — it flows into the informational @wip pass below.
steps.push(
runStep(
"festipod:@multibrowser",
"node",
cucumberArgsByTags("@multibrowser"),
cucumberArgsByTags("@multibrowser and not @wip"),
FESTIPOD_DIR,
TIMEOUT_MULTIBROWSER_MS,
),
@@ -57,6 +57,23 @@ Fonctionnalité: Validation e2e multi-navigateurs des nouvelles features (T02.f)
# A (poussé par doc_subscribe sur le doc public de l'événement) montre
# participantCount === 1 et un participant "inconnu".
# @wip — LIMITE STRUCTURELLE wallet partagé (pas un bug produit)
#
# CAUSE RACINE : en harness @shared-wallet, A et B partagent UNE SEULE identité
# NextGraph. `listMyEntityDocs(username, 'public')` retourne les mêmes docs dans
# les deux contextes Playwright. Le owner-materializer de B détecte donc les
# événements créés par A comme "possédés" et tente d'écrire `participantCount` sur
# le doc de A. Le verifier de B n'a pas ouvert ce repo (il a été créé dans la
# session de A) → le broker retourne `StorageError` sur `sparql_update`. La
# matérialisation échoue côté B, et le compteur ne converge pas dans le délai du
# test. En production, A et B sont des identités distinctes (wallets séparés) et B
# ne possède jamais les événements de A — ce conflit n'existe pas.
#
# FIX ATTENDU : wallet isolation réelle (distinct-wallets, T02.d/T02.g) — chaque
# navigateur a sa propre identité NG ; `listMyEntityDocs` ne retourne que les docs
# de cette identité ; le owner-materializer est naturellement borné à son propre
# wallet. Jusqu'à cette migration, ce scénario reste @wip documenté.
@wip
Scénario: Un participant apparaît réactivement dans l'autre navigateur sans reload
Étant donné un navigateur "A" avec le wallet partagé
Et un navigateur "B" avec le wallet partagé