From b9471d81a676fb30741a84a5ec073b1d39c8bda1 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 23 Oct 2026 23:03:35 -0700 Subject: [PATCH 1/3] chore(quest): claim quest/m0/worker-tests.md Quest-Claim: 563286ff8dda6b06 From 0bcccc21012780ed914c6cd3ddcda6ca3c458bf9 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 23 Oct 2026 23:13:17 -0700 Subject: [PATCH 2/3] test(worker): reset storage before every test and shuffle the order Each Worker test now starts from empty, freshly migrated storage (D1, R2, and Durable Objects via cloudflare:test reset()), so none depends on rows another test left behind. The per-file DELETE FROM cleanups and INSERT OR IGNORE seeds become plain setup. Vitest shuffles files and tests on every run, so CI catches a new order dependence. Co-Authored-By: Claude Opus 5.5 --- cloudflare/test/github.test.ts | 7 ++----- cloudflare/test/inbox.test.ts | 6 ------ cloudflare/test/jobs.test.ts | 9 +-------- cloudflare/test/projects.test.ts | 7 ++----- cloudflare/test/publish.test.ts | 10 +++------- cloudflare/test/push.test.ts | 6 ------ cloudflare/test/runners.test.ts | 3 +-- cloudflare/test/seed.test.ts | 6 ------ cloudflare/test/setup.ts | 11 +++++++++-- cloudflare/test/worker.test.ts | 2 -- cloudflare/vitest.config.ts | 10 +++++++++- 11 files changed, 27 insertions(+), 50 deletions(-) diff --git a/cloudflare/test/github.test.ts b/cloudflare/test/github.test.ts index ffa1d3d6..43c0a1e0 100644 --- a/cloudflare/test/github.test.ts +++ b/cloudflare/test/github.test.ts @@ -23,14 +23,11 @@ async function state() { beforeEach(async () => { await env.DB.batch([ - env.DB.prepare("DELETE FROM github_deliveries"), - env.DB.prepare("DELETE FROM github_projects"), - env.DB.prepare("DELETE FROM github_installations"), env.DB.prepare( - "INSERT OR IGNORE INTO user(id,name,email,emailVerified,createdAt,updatedAt) VALUES('app-test','Tester','app-test@example.com',1,0,0)", + "INSERT INTO user(id,name,email,emailVerified,createdAt,updatedAt) VALUES('app-test','Tester','app-test@example.com',1,0,0)", ), env.DB.prepare( - "INSERT OR IGNORE INTO account(id,accountId,providerId,userId,createdAt,updatedAt) VALUES('app-test-account','7','github','app-test',0,0)", + "INSERT INTO account(id,accountId,providerId,userId,createdAt,updatedAt) VALUES('app-test-account','7','github','app-test',0,0)", ), ]); }); diff --git a/cloudflare/test/inbox.test.ts b/cloudflare/test/inbox.test.ts index 12058b8a..6fca0d3e 100644 --- a/cloudflare/test/inbox.test.ts +++ b/cloudflare/test/inbox.test.ts @@ -18,12 +18,6 @@ let github: Awaited>["github"]; let mock: Awaited>["mock"]; beforeEach(async () => { - await db.batch([ - db.prepare("DELETE FROM jobs"), - db.prepare("DELETE FROM runners"), - db.prepare("DELETE FROM github_projects"), - db.prepare("DELETE FROM github_installations"), - ]); ({ user, cookie, github, mock } = await maintainerOf()); }); afterEach(() => vi.restoreAllMocks()); diff --git a/cloudflare/test/jobs.test.ts b/cloudflare/test/jobs.test.ts index 57dd6cc4..3d59ad71 100644 --- a/cloudflare/test/jobs.test.ts +++ b/cloudflare/test/jobs.test.ts @@ -17,7 +17,7 @@ const testEnv = { ...env, GITHUB_APP_PRIVATE_KEY: privateKey }; const db = env.DB; async function user(id: string) { await db - .prepare("INSERT OR IGNORE INTO user(id,name,email,emailVerified,createdAt,updatedAt) VALUES(?,?,?,1,0,0)") + .prepare("INSERT INTO user(id,name,email,emailVerified,createdAt,updatedAt) VALUES(?,?,?,1,0,0)") .bind(id, id, `${id}@example.test`) .run(); } @@ -62,13 +62,6 @@ async function run(slots: Record, now: number, owner = "owner", } beforeEach(async () => { - await db.batch([ - db.prepare("DELETE FROM jobs"), - db.prepare("DELETE FROM runners"), - db.prepare("DELETE FROM github_projects"), - db.prepare("DELETE FROM github_installations"), - db.prepare("DELETE FROM output_cleanup"), - ]); await user("owner"); await user("other"); await project(10, "owner"); diff --git a/cloudflare/test/projects.test.ts b/cloudflare/test/projects.test.ts index aa22ea63..f0ba6d0c 100644 --- a/cloudflare/test/projects.test.ts +++ b/cloudflare/test/projects.test.ts @@ -232,15 +232,12 @@ async function state() { beforeEach(async () => { await env.DB.batch([ - env.DB.prepare("DELETE FROM github_projects"), - env.DB.prepare("DELETE FROM github_installations"), env.DB.prepare( - "INSERT OR IGNORE INTO user(id,name,email,emailVerified,createdAt,updatedAt) VALUES('enrollment-test','Maintainer','enrollment-test@example.test',1,0,0)", + "INSERT INTO user(id,name,email,emailVerified,createdAt,updatedAt) VALUES('enrollment-test','Maintainer','enrollment-test@example.test',1,0,0)", ), env.DB.prepare( - "INSERT OR IGNORE INTO account(id,accountId,providerId,userId,createdAt,updatedAt) VALUES('enrollment-account','7007','github','enrollment-test',0,0)", + "INSERT INTO account(id,accountId,providerId,userId,createdAt,updatedAt) VALUES('enrollment-account','7007','github','enrollment-test',0,0)", ), - env.DB.prepare("UPDATE account SET accountId='7007' WHERE userId='enrollment-test'"), ]); }); afterEach(() => vi.restoreAllMocks()); diff --git a/cloudflare/test/publish.test.ts b/cloudflare/test/publish.test.ts index 4b73a52d..53653b6f 100644 --- a/cloudflare/test/publish.test.ts +++ b/cloudflare/test/publish.test.ts @@ -333,16 +333,12 @@ async function job(id: string) { beforeEach(async () => { await db.batch([ - db.prepare("DELETE FROM jobs"), - db.prepare("DELETE FROM runners"), - db.prepare("DELETE FROM github_projects"), - db.prepare("DELETE FROM github_installations"), db - .prepare("INSERT OR IGNORE INTO user(id,name,email,emailVerified,createdAt,updatedAt) VALUES(?,?,?,1,0,0)") + .prepare("INSERT INTO user(id,name,email,emailVerified,createdAt,updatedAt) VALUES(?,?,?,1,0,0)") .bind(user, user, `${user}@example.test`), db .prepare( - "INSERT OR IGNORE INTO account(id,accountId,providerId,userId,createdAt,updatedAt) VALUES(?,?,'github',?,0,0)", + "INSERT INTO account(id,accountId,providerId,userId,createdAt,updatedAt) VALUES(?,?,'github',?,0,0)", ) .bind(`${user}-account`, String(githubUser), user), db @@ -840,7 +836,7 @@ describe("runner publication", () => { expect(blocked.status).toBe(202); const question = (await job(id)).questions[0]!.id; await db - .prepare("INSERT OR IGNORE INTO user(id,name,email,emailVerified,createdAt,updatedAt) VALUES(?,?,?,1,0,0)") + .prepare("INSERT INTO user(id,name,email,emailVerified,createdAt,updatedAt) VALUES(?,?,?,1,0,0)") .bind("other", "other", "other@example.test") .run(); const { resumed } = await store.answer(db, id, question, "other", "Keep going", Date.now()); diff --git a/cloudflare/test/push.test.ts b/cloudflare/test/push.test.ts index bef392ba..ec0b6e24 100644 --- a/cloudflare/test/push.test.ts +++ b/cloudflare/test/push.test.ts @@ -126,12 +126,6 @@ describe("decision notifications", () => { let respond: (input: RequestInfo | URL, init?: RequestInit) => Promise; beforeEach(async () => { - await db.batch([ - db.prepare("DELETE FROM push_subscriptions"), - db.prepare("DELETE FROM jobs"), - db.prepare("DELETE FROM runners"), - db.prepare("DELETE FROM github_projects"), - ]); const maintainer = await maintainerOf(); ({ user, cookie, github } = maintainer); sent = []; diff --git a/cloudflare/test/runners.test.ts b/cloudflare/test/runners.test.ts index fbf194c2..bcc58940 100644 --- a/cloudflare/test/runners.test.ts +++ b/cloudflare/test/runners.test.ts @@ -10,7 +10,6 @@ let cookie = ""; let owner = ""; beforeEach(async () => { - await db.prepare("DELETE FROM runners").run(); const who = identity("github"); cookie = (await oauth(who)).session!; owner = (await db @@ -68,7 +67,7 @@ describe("runner login", () => { it("refuses a key another account already connected", async () => { await db - .prepare("INSERT OR IGNORE INTO user(id,name,email,emailVerified,createdAt,updatedAt) VALUES(?,?,?,1,0,0)") + .prepare("INSERT INTO user(id,name,email,emailVerified,createdAt,updatedAt) VALUES(?,?,?,1,0,0)") .bind("someone", "someone", "someone@example.test") .run(); await store.registerRunner(db, "someone", "theirs", "linux", false, await key(), 0); diff --git a/cloudflare/test/seed.test.ts b/cloudflare/test/seed.test.ts index 25d98cc2..db17fe51 100644 --- a/cloudflare/test/seed.test.ts +++ b/cloudflare/test/seed.test.ts @@ -17,12 +17,6 @@ afterEach(() => vi.restoreAllMocks()); /** Seed a fresh world against the fake GitHub, returning it, a request helper, and the session cookie. */ async function seeded() { - await db.batch([ - db.prepare("DELETE FROM jobs"), - db.prepare("DELETE FROM runners"), - db.prepare("DELETE FROM github_projects"), - db.prepare("DELETE FROM github_installations"), - ]); const data = { ...world(), forks: env.GITHUB_FORK_OWNER }; vi.spyOn(globalThis, "fetch").mockImplementation(fakeGitHub(data)); // Webhooks read and notify in the background, so each request finishes its `waitUntil` work. diff --git a/cloudflare/test/setup.ts b/cloudflare/test/setup.ts index 3ce27e0e..d7bc4656 100644 --- a/cloudflare/test/setup.ts +++ b/cloudflare/test/setup.ts @@ -1,3 +1,10 @@ -import { applyD1Migrations } from "cloudflare:test"; +import { applyD1Migrations, reset } from "cloudflare:test"; import { env } from "cloudflare:workers"; -await applyD1Migrations(env.DB, env.TEST_MIGRATIONS); +import { beforeEach } from "vitest"; + +// Every test starts from empty, freshly migrated storage, so none depends on +// rows, objects, or Durable Objects another test left behind. +beforeEach(async () => { + await reset(); + await applyD1Migrations(env.DB, env.TEST_MIGRATIONS); +}); diff --git a/cloudflare/test/worker.test.ts b/cloudflare/test/worker.test.ts index 816c5d45..005cc32b 100644 --- a/cloudflare/test/worker.test.ts +++ b/cloudflare/test/worker.test.ts @@ -120,8 +120,6 @@ describe("Worker", () => { "/%5cevil.example", ].map((next) => [next, "/inbox"]), ])("returns to a local page after signing in: %j -> %j", async (next, expected) => { - // Better Auth stores each sign-in's callback with its OAuth state. - await env.DB.prepare("DELETE FROM verification").run(); const response = await SELF.fetch(`${origin}/sign-in`, { method: "POST", headers: { Origin: origin }, diff --git a/cloudflare/vitest.config.ts b/cloudflare/vitest.config.ts index 5cc3f169..52f29a83 100644 --- a/cloudflare/vitest.config.ts +++ b/cloudflare/vitest.config.ts @@ -40,5 +40,13 @@ export default defineConfig({ // (the pool appends `import main` to cloudflare:test), all transformed by vitest's // one Node process, which also answers a module resolve for every SELF.fetch. // One shared runtime imports the Worker once, so a busy host can't stall the tests. - test: { setupFiles: ["./test/setup.ts"], fileParallelism: false, isolate: false }, + // test/setup.ts resets storage before each test, and every run shuffles files and + // tests, so a test that depends on another's leftovers fails (rerun with the + // printed seed: `vitest run --sequence.seed=`). + test: { + setupFiles: ["./test/setup.ts"], + fileParallelism: false, + isolate: false, + sequence: { shuffle: true }, + }, }); From 13c56a7ca56f6ad94b5614b33466126570e77c08 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 23 Oct 2026 23:13:37 -0700 Subject: [PATCH 3/3] quest: finish m0/worker-tests Co-Authored-By: Claude Opus 5.5 --- quest/m0/README.md | 1 - quest/m0/worker-tests.md | 22 ---------------------- 2 files changed, 23 deletions(-) delete mode 100644 quest/m0/worker-tests.md diff --git a/quest/m0/README.md b/quest/m0/README.md index 98a86a04..a3cd2e03 100644 --- a/quest/m0/README.md +++ b/quest/m0/README.md @@ -36,7 +36,6 @@ ownership-manifest design: ## Required - [Lifecycle flakes](/quest/m0/lifecycle-flakes.md) - lifecycle CI jobs stop failing on Windows `EBUSY` and unstarted macOS jobs -- [Worker tests](/quest/m0/worker-tests.md) - every Worker test passes alone and in any order - [Gate bench](/quest/m0/gate-bench.md) - measure headroom admission on an idle host - [Agent lifecycle integration](/quest/m0/worktree-agents.md) - reclaim completed and suspended checkouts through their harness - [Agent workflow helpers](/quest/m0/helpers/README.md) - CLI commands for CI waits, PR status, claims, quest moves, and serialized merges diff --git a/quest/m0/worker-tests.md b/quest/m0/worker-tests.md deleted file mode 100644 index 3932a516..00000000 --- a/quest/m0/worker-tests.md +++ /dev/null @@ -1,22 +0,0 @@ -# [S] Make every Worker test pass on its own - -## Goal - -Each Worker test (`cloudflare/test/`) passes when run alone, in any order, or -in the full suite, because no test depends on D1 state another test left -behind. - -## Plan - -Decided 2026-10-22 (user) as a follow-up of -[kixelated/quest#188](https://github.com/kixelated/quest/pull/188): - -- A test in `cloudflare/test/publish.test.ts` passed in the full suite but - failed alone with a foreign-key error until it inserted its second user - itself, so D1 state leaks between tests in a full run. -- Find how state is shared (per-file or per-suite storage, seeded rows, - module-level fixtures) and make each test set up what it needs, rather than - fixing tests one at a time as they surface. -- Prove it by running each Worker test case alone (not just each file, since - tests in one file share state too) and the suite in a shuffled order, and - keep a check in CI that would catch a new order dependence.