mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 21:10:59 +00:00
feat(anamnesis): soft-delete AUDIT-C in its route, admit both AUDIT-C verbs at manage level, and settle the guards
This commit is contained in:
1 parent
28a8737124
commit
73bb13a982
26 files changed
+275
-154
No files matched your search
@@ -10,8 +10,9 @@ import { NextRequest } from "next/server";
|
||||
import { apiHandler, requireRecordAuth } from "@/lib/api-handler";
|
||||
import { apiError, apiSuccess, getClientIp } from "@/lib/api-response";
|
||||
import { auditLog } from "@/lib/auth/audit";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { annotate } from "@/lib/logging/context";
|
||||
import { removeAuditC } from "@/lib/profile/life-circumstances";
|
||||
import { invalidateHealthProfileFactConsumers } from "@/lib/profile/health-facts";
|
||||
|
||||
type RouteParams = { params: Promise<{ id: string }> };
|
||||
|
||||
@@ -20,12 +21,20 @@ export const DELETE = apiHandler(
|
||||
const { user } = await requireRecordAuth("manage", "profile");
|
||||
const { id } = await params;
|
||||
|
||||
const removed = await removeAuditC(user.id, id);
|
||||
if (!removed) {
|
||||
// Soft delete, scoped to a live AUDIT-C entry of the record: a foreign
|
||||
// id, a screener's id or an entry already removed is the same 404.
|
||||
const now = new Date();
|
||||
const result = await prisma.mentalHealthAssessment.updateMany({
|
||||
where: { id, userId: user.id, instrument: "AUDIT_C", deletedAt: null },
|
||||
data: { deletedAt: now, syncVersion: { increment: 1 } },
|
||||
});
|
||||
if (result.count !== 1) {
|
||||
return apiError("AUDIT-C entry not found", 404, {
|
||||
errorCode: "anamnesis.auditC.notFound",
|
||||
});
|
||||
}
|
||||
await invalidateHealthProfileFactConsumers(user.id, prisma);
|
||||
const removed = { id, removedAt: now.toISOString() };
|
||||
|
||||
await auditLog("anamnesis.audit_c.delete", {
|
||||
userId: user.id,
|
||||
|
||||
@@ -106,6 +106,8 @@ describe("anamnesis facts API", () => {
|
||||
"user-1",
|
||||
"SMOKING_STATUS",
|
||||
"FORMER",
|
||||
expect.any(Date),
|
||||
undefined,
|
||||
);
|
||||
expect(mocks.audit).toHaveBeenCalledOnce();
|
||||
});
|
||||
@@ -135,7 +137,13 @@ describe("anamnesis facts API", () => {
|
||||
where: expect.objectContaining({ id: "fact-1", userId: "user-1" }),
|
||||
}),
|
||||
);
|
||||
expect(mocks.correct).toHaveBeenCalledWith("user-1", "fact-1", "NEVER");
|
||||
expect(mocks.correct).toHaveBeenCalledWith(
|
||||
"user-1",
|
||||
"fact-1",
|
||||
"NEVER",
|
||||
expect.any(Date),
|
||||
undefined,
|
||||
);
|
||||
expect(mocks.audit).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ vi.mock("@/lib/db", () => ({
|
||||
familyHistoryEntry: { findMany: vi.fn() },
|
||||
userHealthProfile: { findUnique: vi.fn() },
|
||||
healthProfileFactRevision: { findMany: vi.fn() },
|
||||
// v1.43 — the anamnesis leaf also carries the newest AUDIT-C entry.
|
||||
mentalHealthAssessment: { findFirst: vi.fn(async () => null) },
|
||||
user: { findUnique: vi.fn(), update: vi.fn() },
|
||||
// The whole-record gate reads the session's own stamps: a sign-in a
|
||||
// moment ago is the proof, on an account without a second factor.
|
||||
|
||||
@@ -250,7 +250,12 @@ describe("POST /api/mental-health/assessments", () => {
|
||||
expect(body.data.assessment.id).toBe("mha_dup");
|
||||
expect(prisma.mentalHealthAssessment.findFirst).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: { userId: "user-1", externalId: "outbox-7f3c" },
|
||||
// Screeners only: an AUDIT-C entry (v1.43) is never a dedup match.
|
||||
where: {
|
||||
userId: "user-1",
|
||||
externalId: "outbox-7f3c",
|
||||
instrument: { in: ["PHQ9", "GAD7", "WHO5", "SCI"] },
|
||||
},
|
||||
}),
|
||||
);
|
||||
expect(prisma.mentalHealthAssessment.create).not.toHaveBeenCalled();
|
||||
|
||||
Reference in new issue
Block a user