mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 21:32:18 +00:00
test: prove the setup flow against a real database and a real grant
Four claims the unit suite cannot reach, because each is about what is in the database afterwards: the answers persist and a replay writes nothing new, the module derivation happens once and leaves a later Settings change alone, a restart resets the steps and not the modules, and a delegate with a live accepted grant and a switched session is refused on all three writes — with neither record acquiring a row, which is the quiet failure a fall-back to the caller's own record would produce. The demo guard gains the three paths and, beside them, the case that matters more: admitting one verb on a path must not open a sibling.
This commit is contained in:
1 parent
3f44b882ac
commit
3f015effec
3 files changed
+459
-6
No files matched your search
@@ -51,6 +51,31 @@ describe("proxy.ts DEMO_MODE mutation allowlist", () => {
|
||||
expect(res.status).not.toBe(403);
|
||||
});
|
||||
|
||||
it("permits the three setup-flow writes so the demo can finish onboarding", () => {
|
||||
// v1.39 (C1) — the design spec requires the demo to complete the same flow
|
||||
// with preset answers, and none of the three creates health data: closed
|
||||
// enum answers, the step ledger, and the module map derived from them.
|
||||
for (const [path, method] of [
|
||||
["/api/onboarding/answers", "PATCH"],
|
||||
["/api/onboarding/complete", "POST"],
|
||||
["/api/onboarding/restart", "POST"],
|
||||
] as const) {
|
||||
expect(
|
||||
proxy(makeRequest(path, method)).status,
|
||||
`${method} ${path} is blocked in the demo`,
|
||||
).not.toBe(403);
|
||||
}
|
||||
});
|
||||
|
||||
it("does not drag in a sibling verb on an allowlisted onboarding path", () => {
|
||||
// The allowlist pins method per path, so admitting the answers PATCH must
|
||||
// not open a DELETE somebody adds to that route later.
|
||||
expect(proxy(makeRequest("/api/onboarding/answers", "DELETE")).status).toBe(
|
||||
403,
|
||||
);
|
||||
expect(proxy(makeRequest("/api/onboarding/step", "POST")).status).toBe(403);
|
||||
});
|
||||
|
||||
it("still blocks a health-data mutation (POST /api/measurements)", () => {
|
||||
const res = proxy(makeRequest("/api/measurements", "POST"));
|
||||
expect(res.status).toBe(403);
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { NextRequest } from "next/server";
|
||||
|
||||
vi.mock("@/lib/db", () => ({
|
||||
prisma: {
|
||||
user: { update: vi.fn() },
|
||||
auditLog: { create: vi.fn() },
|
||||
},
|
||||
}));
|
||||
vi.mock("@/lib/db", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@/lib/db")>();
|
||||
return {
|
||||
...actual,
|
||||
prisma: {
|
||||
user: { update: vi.fn(), findUnique: vi.fn() },
|
||||
auditLog: { create: vi.fn() },
|
||||
// v1.39 (C1) — the needs half of the route looks for a setup row before
|
||||
// it does anything. These cases are the LEGACY caller, which has none,
|
||||
// so the lookup answers null and the route falls straight through to the
|
||||
// acknowledgement it always returned.
|
||||
onboardingRecord: { findUnique: vi.fn(), update: vi.fn() },
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("@/lib/auth/session", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@/lib/auth/session")>();
|
||||
@@ -58,6 +67,7 @@ beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
vi.mocked(prisma.user.update).mockResolvedValue({} as never);
|
||||
vi.mocked(prisma.auditLog.create).mockResolvedValue({} as never);
|
||||
vi.mocked(prisma.onboardingRecord.findUnique).mockResolvedValue(null);
|
||||
});
|
||||
|
||||
describe("POST /api/onboarding/complete", () => {
|
||||
|
||||
Reference in new issue
Block a user