mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
fix(meds): glp1 GET routes through assertMedicationOwnership
The destructive sweep in commit af224964 missed the glp1 GET
branch; the handler still hand-rolled findUnique + userId compare.
Lifting it to the same ownership helper closes the §10 invariant
24 gap and aligns the GET with the POST.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
043c335ba3
commit
359b103e90
2 files changed
+56
-6
No files matched your search
@@ -19,6 +19,10 @@ vi.mock("@/lib/db", () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/medications/route-guards", () => ({
|
||||
assertMedicationOwnership: vi.fn().mockResolvedValue(null),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() }));
|
||||
|
||||
vi.mock("@/lib/auth/audit", () => ({
|
||||
@@ -49,11 +53,12 @@ vi.mock("next/headers", () => ({
|
||||
})),
|
||||
}));
|
||||
|
||||
import { POST } from "../route";
|
||||
import { GET, POST } from "../route";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { getSession } from "@/lib/auth/session";
|
||||
import { auditLog } from "@/lib/auth/audit";
|
||||
import { checkRateLimit } from "@/lib/rate-limit";
|
||||
import { assertMedicationOwnership } from "@/lib/medications/route-guards";
|
||||
|
||||
const SESSION_OK = {
|
||||
session: { id: "sess-1", expiresAt: new Date(Date.now() + 3_600_000) },
|
||||
@@ -77,6 +82,7 @@ beforeEach(() => {
|
||||
remaining: 29,
|
||||
resetAt: Date.now() + 60_000,
|
||||
});
|
||||
vi.mocked(assertMedicationOwnership).mockResolvedValue(null);
|
||||
});
|
||||
|
||||
describe("POST /api/medications/[id]/glp1", () => {
|
||||
@@ -97,10 +103,9 @@ describe("POST /api/medications/[id]/glp1", () => {
|
||||
|
||||
it("returns 404 when the medication belongs to a different user", async () => {
|
||||
vi.mocked(getSession).mockResolvedValue(SESSION_OK as never);
|
||||
vi.mocked(prisma.medication.findUnique).mockResolvedValue({
|
||||
id: "med-1",
|
||||
userId: "OTHER",
|
||||
} as never);
|
||||
vi.mocked(assertMedicationOwnership).mockResolvedValueOnce(
|
||||
new Response(null, { status: 404 }) as never,
|
||||
);
|
||||
const res = await POST(
|
||||
jsonReq({
|
||||
doseChange: {
|
||||
@@ -387,3 +392,39 @@ describe("POST /api/medications/[id]/glp1 — 422 multi-issue (v1.4.43 W6)", ()
|
||||
expect(body.details.issues.length).toBeGreaterThanOrEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe("GET /api/medications/[id]/glp1 — ownership helper (F-1 C-4)", () => {
|
||||
function getReq(): NextRequest {
|
||||
return new NextRequest("http://localhost/api/medications/med-1/glp1");
|
||||
}
|
||||
|
||||
it("routes ownership through assertMedicationOwnership", async () => {
|
||||
vi.mocked(getSession).mockResolvedValue(SESSION_OK as never);
|
||||
vi.mocked(prisma.medication.findUnique).mockResolvedValue({
|
||||
id: "med-1",
|
||||
userId: "user-1",
|
||||
doseChanges: [],
|
||||
inventoryEvents: [],
|
||||
intakeEvents: [],
|
||||
schedules: [],
|
||||
dosesPerUnit: null,
|
||||
} as never);
|
||||
const res = await GET(getReq(), {
|
||||
params: Promise.resolve({ id: "med-1" }),
|
||||
});
|
||||
expect(res.status).toBe(200);
|
||||
expect(assertMedicationOwnership).toHaveBeenCalledWith("med-1", "user-1");
|
||||
});
|
||||
|
||||
it("returns the shared helper's 404 without reading the medication", async () => {
|
||||
vi.mocked(getSession).mockResolvedValue(SESSION_OK as never);
|
||||
vi.mocked(assertMedicationOwnership).mockResolvedValueOnce(
|
||||
new Response(null, { status: 404 }) as never,
|
||||
);
|
||||
const res = await GET(getReq(), {
|
||||
params: Promise.resolve({ id: "med-1" }),
|
||||
});
|
||||
expect(res.status).toBe(404);
|
||||
expect(prisma.medication.findUnique).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -41,6 +41,15 @@ export const GET = apiHandler(
|
||||
const { user } = await requireAuth();
|
||||
const { id } = await params;
|
||||
|
||||
// v1.5.5 F-1 C-4 — close the §10 invariant 24 gap. The destructive
|
||||
// sweep in af224964 lifted every sibling `[id]/**` handler onto the
|
||||
// shared ownership helper; the glp1 GET still hand-rolled the
|
||||
// `findUnique + userId compare` pattern. Routing it through the
|
||||
// same predicate keeps the 404 leak shape identical across every
|
||||
// handler in the directory.
|
||||
const guard = await assertMedicationOwnership(id, user.id);
|
||||
if (guard) return guard;
|
||||
|
||||
const medication = await prisma.medication.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
@@ -56,7 +65,7 @@ export const GET = apiHandler(
|
||||
},
|
||||
});
|
||||
|
||||
if (!medication || medication.userId !== user.id) {
|
||||
if (!medication) {
|
||||
return apiError("Medication not found", 404);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user