diff --git a/src/__tests__/handler/schema-guard.test.ts b/src/__tests__/handler/schema-guard.test.ts index 4487794..53a1d28 100644 --- a/src/__tests__/handler/schema-guard.test.ts +++ b/src/__tests__/handler/schema-guard.test.ts @@ -308,6 +308,22 @@ describe("GET /_/health", () => { expect(body.status).toBe("degraded"); expect(body.schema.error).toContain("D1 is unavailable right now"); }); + + it("recovers once a fresh live read reports the schema ready, even with a failure remembered from before", async () => { + // This isolate's first attempt hits a transient error and remembers it. + await worker.fetch(req("/x"), { ...env, DB: brokenDb("D1 is unavailable right now") }, createExecutionContext()); + // The schema is then genuinely applied for real, the way another isolate + // racing the same cold start would: through migrate() directly, without + // this isolate's ensureSchema() ever running again to clear its memo. + await migrate(env); + + const res = await SELF.fetch(req("/_/health")); + expect(res.status).toBe(200); + const body = (await res.json()) as { status: string; schema: { ready: boolean; error?: string } }; + expect(body.status).toBe("ok"); + expect(body.schema.ready).toBe(true); + expect(body.schema.error).toBeUndefined(); + }); }); describe("/_/setup", () => { diff --git a/src/api/health.ts b/src/api/health.ts index d128ee0..175be50 100644 --- a/src/api/health.ts +++ b/src/api/health.ts @@ -25,6 +25,11 @@ export async function handleHealth(env: Env): Promise { const status = await schemaStatus(env); applied = status.applied.length > 0 ? status.applied[status.applied.length - 1].name : null; ready = status.ready; + // A live read that finds every migration applied is direct proof the + // schema is fine right now, even on an isolate that still remembers an + // earlier attempt failing (its own, or one it never repeated because + // health is exempt from ensureSchema()). + if (ready) error = null; } catch (err) { error = err instanceof Error ? err.message : String(err); }