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 173be45f..4787a568 100644 --- a/cloudflare/test/inbox.test.ts +++ b/cloudflare/test/inbox.test.ts @@ -20,12 +20,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 2ad09c2c..69891353 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 8e86e8f3..3030386d 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 0de46cbb..931ad6bc 100644 --- a/cloudflare/test/publish.test.ts +++ b/cloudflare/test/publish.test.ts @@ -152,16 +152,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 @@ -693,7 +689,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 8c4ad9f8..30d23b3a 100644 --- a/cloudflare/test/seed.test.ts +++ b/cloudflare/test/seed.test.ts @@ -20,12 +20,6 @@ afterEach(() => vi.restoreAllMocks()); * returning it, a request helper, and the session cookie. */ async function seeded(edit: (world: World) => void = () => {}) { - 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 }; edit(data); vi.spyOn(globalThis, "fetch").mockImplementation(fakeGitHub(data)); 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 }, + }, }); diff --git a/quest/m0/README.md b/quest/m0/README.md index 7e26660a..63ec49b0 100644 --- a/quest/m0/README.md +++ b/quest/m0/README.md @@ -35,7 +35,6 @@ ownership-manifest design: ## Required -- [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.