mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 21:10:59 +00:00
test(soft-delete): pin gamification/achievements and doctor-report/availability
The W-DELETED-2 integration-test file header promises coverage for five reader tiers; v1.4.41 shipped pin assertions for three (`/api/export`, `/api/export/full-backup`, `/api/export/measurements`) but never asserted the two routes that the W-DELETED-2 fix actually touched: - `/api/gamification/achievements` — reads recent measurements via `findMany` to compute streak / badge progress. A tombstoned row must never count toward a milestone. - `/api/doctor-report/availability` — four parallel `measurement.count` probes drive the section-availability switch. A soft-deleted history must not light up a section the user has since wiped. Adds one `it()` block per route asserting that every measurement read scopes to `deletedAt: null`. Mocks already in place; no test-infra work needed.
This commit is contained in:
1 parent
92753a7096
commit
9d8e0d7a07
1 file changed
+47
@@ -37,6 +37,9 @@ vi.mock("@/lib/db", () => ({
|
||||
|
||||
vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() }));
|
||||
vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn() }));
|
||||
vi.mock("@/lib/auth", () => ({
|
||||
requireAuth: vi.fn().mockResolvedValue({ user: { id: "user-1" } }),
|
||||
}));
|
||||
vi.mock("@/lib/auth/audit", () => ({
|
||||
auditLog: vi.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
@@ -114,4 +117,48 @@ describe("v1.4.41 W-DELETED-2 — soft-delete invisibility", () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("/api/gamification/achievements scopes the measurement read to deletedAt: null", async () => {
|
||||
vi.mocked(prisma.userAchievement.findMany).mockResolvedValue([] as never);
|
||||
const { GET } = await import(
|
||||
"../../gamification/achievements/route"
|
||||
);
|
||||
await GET(mkReq("http://localhost/api/gamification/achievements"));
|
||||
const calls = vi.mocked(prisma.measurement.findMany).mock.calls;
|
||||
// The achievement aggregator reads recent measurements via
|
||||
// findMany — a tombstoned row must never count toward a streak
|
||||
// or PR badge.
|
||||
expect(calls.length).toBeGreaterThan(0);
|
||||
for (const [arg] of calls) {
|
||||
expect(arg).toMatchObject({
|
||||
where: expect.objectContaining({ deletedAt: null }),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("/api/doctor-report/availability scopes every measurement count to deletedAt: null", async () => {
|
||||
const { POST } = await import(
|
||||
"../../doctor-report/availability/route"
|
||||
);
|
||||
const req = new NextRequest(
|
||||
"http://localhost/api/doctor-report/availability",
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({}),
|
||||
},
|
||||
);
|
||||
await POST(req);
|
||||
const calls = vi.mocked(prisma.measurement.count).mock.calls;
|
||||
// The probe runs four parallel measurement.count queries (BP /
|
||||
// weight / pulse / sleep). All must filter tombstoned rows so a
|
||||
// soft-deleted history does not light up a section the user has
|
||||
// since wiped.
|
||||
expect(calls.length).toBeGreaterThanOrEqual(4);
|
||||
for (const [arg] of calls) {
|
||||
expect(arg).toMatchObject({
|
||||
where: expect.objectContaining({ deletedAt: null }),
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user