import { beforeEach, describe, expect, it, vi } from "vitest"; import { recordAuditLog } from "@/modules/core/server/audit"; import { requirePermission } from "@/modules/core/server/auth"; import type { AuthContext } from "@/modules/core/types"; import { createChronicCondition, createVitalRecord, listHealthAdminData, updateHealthReview, upsertHealthProfile, } from "@/modules/health/server/operations"; vi.mock("@/modules/core/server/auth", () => ({ requirePermission: vi.fn(), })); vi.mock("@/modules/core/server/audit", () => ({ recordAuditLog: vi.fn(), })); vi.mock("@/modules/health/server/operations", () => ({ createChronicCondition: vi.fn(), createVitalRecord: vi.fn(), isMutationFailure: vi.fn((value: unknown) => typeof value === "object" && value !== null && "success" in value && value.success === false), listHealthAdminData: vi.fn(), updateHealthReview: vi.fn(), upsertHealthProfile: vi.fn(), })); const account: AuthContext["account"] = { id: "account-1", name: "Admin", email: "admin@example.com", avatarUrl: "", role: "org_admin", status: "active", createdAt: "2026-07-02T00:00:00.000Z", updatedAt: "2026-07-02T00:00:00.000Z", }; function createAuthContext(overrides: Partial = {}): AuthContext { return { account, organization: { id: "org-1", name: "Demo Org", slug: "demo-org", status: "active", registrationEnabled: true, oidcEnabled: false, oidcIssuerUrl: "", oidcClientId: "", oidcHasClientSecret: false, oidcScopes: "openid profile email", oidcRedirectUri: "", oidcAvatarClaim: "picture", oidcAutoProvision: false, createdAt: "2026-07-02T00:00:00.000Z", updatedAt: "2026-07-02T00:00:00.000Z", }, organizations: [], permissions: ["health:read", "health:manage"], session: { id: "session-1", accountId: "account-1", activeOrganizationId: "org-1", createdAt: "2026-07-02T00:00:00.000Z", expiresAt: "2026-07-09T00:00:00.000Z", }, ...overrides, }; } const authContext = createAuthContext(); const healthAdminData = { chronicConditions: [], elders: [], metrics: { eldersWithProfiles: 0, pendingReviews: 0, activeChronicConditions: 0, vitalsToday: 0, }, profiles: [], reviews: [], vitals: [], }; function allowAuth(): void { vi.mocked(requirePermission).mockResolvedValue({ success: true, context: authContext, }); } function jsonRequest(body: Record): Request { return new Request("http://localhost/api/health", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body), }); } async function readJson(response: Response): Promise> { return (await response.json()) as Record; } beforeEach(() => { vi.clearAllMocks(); allowAuth(); }); describe("health admin API routes", () => { it("loads health admin data for the active organization", async () => { vi.mocked(listHealthAdminData).mockResolvedValue(healthAdminData); const { GET } = await import("./admin/route"); const response = await GET(); const body = await readJson(response); expect(response.status).toBe(200); expect(body.success).toBe(true); expect(body.data).toEqual(healthAdminData); expect(listHealthAdminData).toHaveBeenCalledWith("org-1"); }); it("returns permission failures before loading admin data", async () => { const denied = Response.json({ success: false, reason: "权限不足" }, { status: 403 }); vi.mocked(requirePermission).mockResolvedValue({ success: false, response: denied }); const { GET } = await import("./admin/route"); const response = await GET(); const body = await readJson(response); expect(response.status).toBe(403); expect(body.reason).toBe("权限不足"); expect(listHealthAdminData).not.toHaveBeenCalled(); }); it("requires an active organization before loading admin data", async () => { vi.mocked(requirePermission).mockResolvedValue({ success: true, context: createAuthContext({ organization: undefined }), }); const { GET } = await import("./admin/route"); const response = await GET(); const body = await readJson(response); expect(response.status).toBe(400); expect(body.reason).toBe("请选择机构后查看健康数据"); expect(listHealthAdminData).not.toHaveBeenCalled(); }); it("upserts one elder health profile", async () => { vi.mocked(upsertHealthProfile).mockResolvedValue({ allergyNotes: "青霉素过敏", careRestrictions: "低盐饮食", createdAt: "2026-07-02T00:00:00.000Z", elderId: "elder-1", elderName: "王桂兰", emergencyNotes: "胸闷需立即复核", id: "profile-1", medicalHistory: "高血压", medicationNotes: "晨服降压药", organizationId: "org-1", updatedAt: "2026-07-02T00:00:00.000Z", }); const { PUT } = await import("./profiles/[elderId]/route"); const response = await PUT(jsonRequest({ allergyNotes: "青霉素过敏" }), { params: Promise.resolve({ elderId: "elder-1" }), }); const body = await readJson(response); expect(response.status).toBe(200); expect(body.success).toBe(true); expect(upsertHealthProfile).toHaveBeenCalledWith( expect.objectContaining({ elderId: "elder-1", organizationId: "org-1", }), ); expect(recordAuditLog).toHaveBeenCalledWith(expect.objectContaining({ action: "health.profile.upsert" })); }); it("rejects invalid vital input", async () => { const { POST } = await import("./vitals/route"); const response = await POST(jsonRequest({ elderId: "elder-1", recordedAt: "bad-date", source: "manual" })); const body = await readJson(response); expect(response.status).toBe(400); expect(body.reason).toBe("记录时间无效"); expect(createVitalRecord).not.toHaveBeenCalled(); expect(recordAuditLog).not.toHaveBeenCalled(); }); it("creates a vital record", async () => { vi.mocked(createVitalRecord).mockResolvedValue({ review: { createdAt: "2026-07-02T00:00:00.000Z", description: "血压偏高", elderId: "elder-1", elderName: "王桂兰", id: "review-1", organizationId: "org-1", resolutionNotes: "", reviewedAt: undefined, reviewedByAccountId: undefined, reviewedByName: undefined, severity: "warning", status: "pending", title: "血压异常", updatedAt: "2026-07-02T00:00:00.000Z", vitalRecordId: "vital-1", }, vital: { bloodGlucoseTenths: 62, createdAt: "2026-07-02T00:00:00.000Z", createdByAccountId: "account-1", createdByName: "Admin", diastolicBp: 96, elderId: "elder-1", elderName: "王桂兰", heartRate: 88, id: "vital-1", notes: "复测", organizationId: "org-1", recordedAt: "2026-07-02T09:00:00.000Z", source: "manual", spo2: 96, systolicBp: 158, temperatureTenths: 367, updatedAt: "2026-07-02T00:00:00.000Z", weightTenths: 612, }, }); const { POST } = await import("./vitals/route"); const response = await POST( jsonRequest({ elderId: "elder-1", recordedAt: "2026-07-02T09:00:00.000Z", source: "manual", systolicBp: 158, diastolicBp: 96, }), ); const body = await readJson(response); expect(response.status).toBe(201); expect(body.success).toBe(true); expect(createVitalRecord).toHaveBeenCalledWith( expect.objectContaining({ accountId: "account-1", organizationId: "org-1", }), ); expect(recordAuditLog).toHaveBeenCalledWith(expect.objectContaining({ action: "health.vital.create" })); }); it("creates a chronic condition", async () => { vi.mocked(createChronicCondition).mockResolvedValue({ createdAt: "2026-07-02T00:00:00.000Z", diagnosedAt: "2024-01-01T00:00:00.000Z", elderId: "elder-1", elderName: "王桂兰", followUpNotes: "每周复查", id: "condition-1", name: "高血压", organizationId: "org-1", status: "active", treatmentNotes: "规律用药", updatedAt: "2026-07-02T00:00:00.000Z", }); const { POST } = await import("./chronic-conditions/route"); const response = await POST( jsonRequest({ diagnosedAt: "2024-01-01T00:00:00.000Z", elderId: "elder-1", name: "高血压", status: "active", }), ); const body = await readJson(response); expect(response.status).toBe(201); expect(body.success).toBe(true); expect(createChronicCondition).toHaveBeenCalledWith(expect.objectContaining({ organizationId: "org-1" })); expect(recordAuditLog).toHaveBeenCalledWith(expect.objectContaining({ action: "health.condition.create" })); }); it("returns structured failures for missing or cross-organization chronic condition elders", async () => { vi.mocked(createChronicCondition).mockResolvedValue({ success: false, reason: "老人档案不存在", status: 404, }); const { POST } = await import("./chronic-conditions/route"); const response = await POST( jsonRequest({ elderId: "elder-from-other-org", name: "高血压", status: "active", }), ); const body = await readJson(response); expect(response.status).toBe(404); expect(body.reason).toBe("老人档案不存在"); expect(recordAuditLog).not.toHaveBeenCalled(); }); it("updates an anomaly review", async () => { vi.mocked(updateHealthReview).mockResolvedValue({ createdAt: "2026-07-02T00:00:00.000Z", description: "血压偏高", elderId: "elder-1", elderName: "王桂兰", id: "review-1", organizationId: "org-1", resolutionNotes: "已复测并通知医生", reviewedAt: "2026-07-02T10:00:00.000Z", reviewedByAccountId: "account-1", reviewedByName: "Admin", severity: "warning", status: "reviewed", title: "血压异常", updatedAt: "2026-07-02T10:00:00.000Z", vitalRecordId: "vital-1", }); const { PATCH } = await import("./reviews/[id]/route"); const response = await PATCH(jsonRequest({ status: "reviewed", resolutionNotes: "已复测并通知医生" }), { params: Promise.resolve({ id: "review-1" }), }); const body = await readJson(response); expect(response.status).toBe(200); expect(body.success).toBe(true); expect(updateHealthReview).toHaveBeenCalledWith( expect.objectContaining({ accountId: "account-1", id: "review-1", organizationId: "org-1", }), ); expect(recordAuditLog).toHaveBeenCalledWith(expect.objectContaining({ action: "health.review.update" })); }); it("returns structured failures for missing or cross-organization reviews", async () => { vi.mocked(updateHealthReview).mockResolvedValue({ success: false, reason: "异常复核记录不存在", status: 404, }); const { PATCH } = await import("./reviews/[id]/route"); const response = await PATCH(jsonRequest({ status: "reviewed", resolutionNotes: "已复核" }), { params: Promise.resolve({ id: "review-from-other-org" }), }); const body = await readJson(response); expect(response.status).toBe(404); expect(body.reason).toBe("异常复核记录不存在"); expect(recordAuditLog).not.toHaveBeenCalled(); }); });