diff --git a/AGENTS.md b/AGENTS.md index df288d6b5a..3660ea1a65 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -219,7 +219,7 @@ Playwright's `beforeAll` runs once **per worker**, not once per test run. When a ```typescript test.beforeAll(async ({ rhdh }) => { - await test.runOnce("tech-radar-setup", async () => { + await test.runOnce(`tech-radar-setup-${rhdh.deploymentConfig.namespace}`, async () => { await rhdh.configure({ auth: "keycloak" }); // Expensive: deploys an external service to the cluster @@ -244,7 +244,7 @@ If a test **does** need an env var that was set inside `runOnce`, extract it fro ```typescript test.beforeAll(async ({ rhdh }) => { - await test.runOnce("my-setup", async () => { + await test.runOnce(`my-setup-${rhdh.deploymentConfig.namespace}`, async () => { await rhdh.configure({ auth: "keycloak" }); await $`bash deploy-service.sh ${rhdh.deploymentConfig.namespace}`; await rhdh.deploy(); @@ -258,8 +258,23 @@ test.beforeAll(async ({ rhdh }) => { ``` **Key rules:** -- The `key` (first argument) must be **globally unique** across all spec files and projects. Prefix with workspace name: `"tech-radar-setup"`, `"argocd-deploy"`. -- Nesting is safe — `deploy()` uses `runOnce` internally, wrapping it in an outer `runOnce` is harmless. +- The `key` (first argument) must be **globally unique** across all spec files **and projects**. A workspace prefix covers the first half; the second half is what bit us. The flag file is keyed by the key string alone, in a directory keyed only on the Playwright runner PID: + + ```ts + const flagDir = path.join(os.tmpdir(), `playwright-once-${process.ppid}`); + const flagFile = path.join(flagDir, `${key}.done`); + ``` + + Nothing in that path comes from the project. So when one spec runs in two projects — which is what adding an `-app-next` lane does — the first project's setup satisfies the second, and the second skips its own. For a block that deploys, that means no deployment at all, then a failure much later on a missing element with nothing pointing at the cause (#3318). + +- **End the key with the namespace whenever the setup belongs to one project**, which is what `deploy()` does internally (`deploy-${namespace}`) and why `deploy()` was never affected: + + ```typescript + await test.runOnce(`my-plugin-setup-${rhdh.deploymentConfig.namespace}`, async () => { ... }); + ``` + + A literal key is right when the setup really is shared — an operator installed once into a fixed namespace every project then uses. `bulk-import` has one of each, deliberately. The key is where you say which you mean. +- Nesting is safe — `deploy()` uses `runOnce` internally, wrapping it in an outer `runOnce` is harmless. It does **not** rescue a project-shared outer key, though: that skips before `deploy()` is reached, so its own protection never gets a say. - Uses file-based flags in `/tmp/` scoped to the Playwright runner process. Flags reset automatically between test runs. ### RHDH Deployment Flow