Security Scan (SIAX Cloud) / security (push) Successful in 21s
CI (SIAX Cloud) / security (push) Successful in 21s
CI (SIAX Cloud) / quality (push) Successful in 41s
CI (SIAX Cloud) / security (pull_request) Successful in 22s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 21s
CI (SIAX Cloud) / quality (pull_request) Successful in 41s
BACKLOG P0-3: /api/health only ever checked *presence* of DATABASE_URL/
REDIS_URL and always returned ok:true regardless of whether anything was
reachable -- ARCHITECTURE.md's own words, "an uptime monitor watching HTTP
200 would show green for a dying instance".
lib/health-checks.ts adds real dependency probes:
- db: a live `select 1` round trip (not just DATABASE_URL presence)
- engine: a real HTTP call to the FastAPI engine's own GET /health
- stripe: Stripe's documented lightweight balance.retrieve() liveness call
- queue: honestly reports "not_integrated" when REDIS_URL is set but
unused -- no Redis client is wired anywhere in this repo, so reporting
"ok" would fabricate a check that doesn't exist
/api/health stays a liveness check (always 200 while the process can
respond; the body's `ok` field is the honest signal). New GET /api/ready
fails closed with 503 when the load-bearing dependency (db) isn't both
configured and reachable -- the "no /ready route" gap.
contracts/openapi.yaml, docs/architecture/ARCHITECTURE.md, and docs/slo.md
updated to match. 17 new unit tests (dependency-injected fakes, no real
network/DB) + 5 route-level tests.
35 lines
1.0 KiB
TypeScript
35 lines
1.0 KiB
TypeScript
/**
|
|
* Route-level test for GET /api/ready (BACKLOG P0-3: "no /ready route
|
|
* existed at all"). Unlike /api/health, readiness fails closed: 503 when the
|
|
* load-bearing dependency (db) is not both configured and reachable.
|
|
*/
|
|
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
import { GET as readyGET } from "@/app/api/ready/route";
|
|
|
|
describe("GET /api/ready", () => {
|
|
const savedEnv = {
|
|
DATABASE_URL: process.env.DATABASE_URL,
|
|
POSTGRES_URL: process.env.POSTGRES_URL,
|
|
};
|
|
|
|
beforeEach(() => {
|
|
delete process.env.DATABASE_URL;
|
|
delete process.env.POSTGRES_URL;
|
|
});
|
|
|
|
afterEach(() => {
|
|
for (const [k, v] of Object.entries(savedEnv)) {
|
|
if (v === undefined) delete process.env[k];
|
|
else process.env[k] = v;
|
|
}
|
|
});
|
|
|
|
it("fails closed with 503 when the database is not configured", async () => {
|
|
const res = await readyGET();
|
|
expect(res.status).toBe(503);
|
|
const body = await res.json();
|
|
expect(body.ready).toBe(false);
|
|
expect(body.db).toBe("not_configured");
|
|
});
|
|
});
|