test+seed: fresh virtual wallet per @data scenario + seed events reach the index
- @data Before hook sets a UNIQUE virtual-wallet id (username) per scenario so each scenario starts on a fresh, empty virtual wallet — isolation without touching the physical wallet; "le portefeuille est vide" is now a fast check, not a full scan. resetDataState / clearWallet fan-out dropped. - bootstrapWallet now submits each seeded PUBLIC event to the discovery index (mirrors the product createEvent), so a fresh virtual wallet can see seeded events through discovery rather than as its own docs. Note: @data still red — seeded/published events do not surface in the discovery read (submit→readIndex round-trip against the real broker), and some publish steps time out. The 75s ORM hang is gone; this is a distinct discovery-index integration issue, still under diagnosis.
This commit is contained in:
+38
-19
@@ -9,6 +9,24 @@ import { pool } from './browserPool';
|
||||
|
||||
setDefaultTimeout(90000);
|
||||
|
||||
// PER-SCENARIO FRESH VIRTUAL WALLET (T03.k). The shim keys each emulated account
|
||||
// (its own private virtual wallet) by the NORMALIZED app-level username read from
|
||||
// localStorage['festipod.account.username'] on the harness origin. When every
|
||||
// @data scenario logs in as the SAME fixed user, that ONE virtual wallet
|
||||
// accumulates every doc any prior scenario/run ever wrote → per-doc anchored
|
||||
// reads fan out over hundreds of docs → 90s timeouts. Giving each scenario a
|
||||
// UNIQUE username hands it a FRESH, EMPTY virtual wallet, so reads stay O(what
|
||||
// THIS scenario provisions) and are fast + independent. A monotonic counter +
|
||||
// per-run nonce guarantees uniqueness within and across runs; it normalizes to
|
||||
// itself (lowercase, `@`-free) and is disjoint from the reserved `@index`
|
||||
// account (whose shim key uses a sentinel prefix `normalizeUsername` can't emit).
|
||||
const RUN_NONCE = Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
|
||||
let scenarioSeq = 0;
|
||||
function freshScenarioUsername(): string {
|
||||
scenarioSeq += 1;
|
||||
return `test-${RUN_NONCE}-${scenarioSeq}`;
|
||||
}
|
||||
|
||||
let browser: Browser;
|
||||
let browserContext: BrowserContext;
|
||||
// Non-persistent launcher for fresh, isolated contexts (multi-browser scenarios).
|
||||
@@ -561,6 +579,20 @@ Before({ timeout: 60000 }, async function (this: FestipodWorld, scenario) {
|
||||
// the run self-heals instead of cascading failures across the rest.
|
||||
this.page = await newWalletPageResilient();
|
||||
|
||||
// FRESH VIRTUAL WALLET per scenario (see freshScenarioUsername above). Set a
|
||||
// UNIQUE app-level username into localStorage['festipod.account.username'] on
|
||||
// EVERY origin (the init script runs in each frame before its scripts do —
|
||||
// including the harness iframe on 127.0.0.1). At mount the harness's
|
||||
// AccountStore.get() then reads THIS fresh username, so `if (!username)
|
||||
// login(DEFAULT_HARNESS_USER)` is skipped and the scenario runs on a fresh,
|
||||
// empty virtual wallet. Overwrites any value persisted in the Chromium profile
|
||||
// (init scripts run on each navigation), so no accumulated wallet leaks in.
|
||||
const freshUser = freshScenarioUsername();
|
||||
(this as any).freshUser = freshUser;
|
||||
await this.page.addInitScript((u: string) => {
|
||||
try { window.localStorage.setItem('festipod.account.username', u); } catch { /* opaque origin */ }
|
||||
}, freshUser);
|
||||
|
||||
// Capture console for debugging
|
||||
this.page.on('pageerror', (err) => console.error('[Browser error]', err.message));
|
||||
this.page.on('console', (msg) => {
|
||||
@@ -579,25 +611,12 @@ Before({ timeout: 60000 }, async function (this: FestipodWorld, scenario) {
|
||||
{ timeout: 30000 },
|
||||
);
|
||||
|
||||
// PER-SCENARIO STATE ISOLATION (T03.j). The @data suite shares ONE
|
||||
// persistent broker-backed wallet, so the emulated account registry
|
||||
// ACCUMULATES every account any prior scenario/run created — growing the
|
||||
// read fan-out (`allAccounts()` → per-account `listEntityDocs`) until it
|
||||
// gets slow and flaky. Purge the registry anchor once here so each @data
|
||||
// scenario starts from a CLEAN registry and the fan-out stays bounded to
|
||||
// what this scenario re-provisions. A single SPARQL DELETE on ONE graph —
|
||||
// not a fan-out delete.
|
||||
// HARD-BOUNDED (≤10s): this reset shares the Before hook's 60s budget with
|
||||
// the (already slow, intermittent) broker login. It must NEVER contend for
|
||||
// that budget — a slow purge on a hugely-accumulated anchor graph, or a
|
||||
// broker stall, is swallowed and the scenario proceeds (its own steps still
|
||||
// gate on state). So race it against a 10s cap and never let it throw.
|
||||
await Promise.race([
|
||||
this.appFrame.evaluate(async () => {
|
||||
try { await (window as any).__testData?.resetDataState?.(); } catch { /* best-effort */ }
|
||||
}),
|
||||
new Promise((r) => setTimeout(r, 10000)),
|
||||
]).catch(() => { /* best-effort */ });
|
||||
// NO per-scenario registry/wallet reset needed anymore (was T03.j
|
||||
// resetDataState). Each @data scenario now runs under a UNIQUE username
|
||||
// (freshScenarioUsername, set into localStorage above), so the shim hands it
|
||||
// a FRESH, EMPTY virtual wallet whose account registry starts empty by
|
||||
// construction — nothing to purge. This also drops the ≤10s reset cost that
|
||||
// shared the Before hook's budget with the (slow) broker login.
|
||||
} else {
|
||||
// Mock mode: load harness directly
|
||||
await this.page!.setContent('<!DOCTYPE html><html><body><div id="root"></div></body></html>');
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
seedUsers,
|
||||
} from '../data/seedData';
|
||||
import { writeEntity, ENTITY_TYPE, str, int, flt, bool } from '../data/entityWrites';
|
||||
import { submitEventToIndex } from '../data/discovery';
|
||||
|
||||
/** Scope of a seed entity + how to create its own document (SDK create). */
|
||||
export type Scope = 'public' | 'protected' | 'private';
|
||||
@@ -105,6 +106,12 @@ export async function bootstrapWallet(
|
||||
coverImage: str(e.coverImage), hostName: str(e.hostName), hostInitials: str(e.hostInitials),
|
||||
});
|
||||
eventIdMap.set(e.id, id);
|
||||
// Make the seeded PUBLIC event discoverable, exactly like the product's
|
||||
// createEvent: submit its reference to the global discovery index. Awaited so
|
||||
// the index is populated before any read (a fresh virtual wallet has no "own"
|
||||
// seed docs — it sees the seeded events only through discovery).
|
||||
await submitEventToIndex({ doc: graph, id, title: e.title }, null)
|
||||
.catch(err => console.error('[Bootstrap] submit seed event to index failed:', err));
|
||||
}));
|
||||
console.log('[Bootstrap] Seeded', eventIdMap.size, 'events (participations created live)');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user