Files
teatea-pension/modules/ai/types.test.ts

176 lines
5.5 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { AiCitation, ElderAiAnalysisOutput, ElderAiFinding, ElderAiRecommendation, KnowledgeEntryInput } from "@/modules/ai/types";
import {
parseDataScopes,
validateElderAiAnalysisOutput,
validateKnowledgeEntryInput,
} from "@/modules/ai/types";
const validKnowledgeInput: KnowledgeEntryInput = {
scope: "organization",
organizationId: "org-1",
title: "Fall prevention guidance",
category: "Safety",
tags: "fall,night",
body: "Residents with night wandering need bed exit checks and clear walkways.",
status: "enabled",
};
const validFinding: ElderAiFinding = {
category: "fall-risk",
severity: "warning",
evidence: "Night wandering and prior fall history are present.",
citationIds: ["resident-1"],
};
const validRecommendation: ElderAiRecommendation = {
title: "Increase night checks",
priority: "high",
rationale: "Additional observation reduces missed night wandering events.",
suggestedNextStep: "Add a night-shift round note for the next care review.",
citationIds: ["resident-1"],
};
const validCitation: AiCitation = {
id: "resident-1",
sourceType: "resident_context",
sourceId: "elder-1",
title: "Resident profile",
excerpt: "Night wandering and prior fall history.",
};
const validAnalysisOutput: ElderAiAnalysisOutput = {
overallRiskLevel: "medium",
summary: "Resident has elevated fall risk at night.",
keyFindings: [validFinding],
recommendations: [validRecommendation],
dataGaps: ["Recent gait assessment is unavailable."],
citations: [validCitation],
confidence: 0.72,
modelSummary: {
provider: "openai-compatible",
chatModel: "gpt-test",
knowledgeRetrieval: "keyword",
},
};
describe("AI validators", () => {
describe("validateKnowledgeEntryInput", () => {
it("accepts a scoped entry and trims user-authored text fields", () => {
const result = validateKnowledgeEntryInput({
...validKnowledgeInput,
title: " Fall prevention guidance ",
category: " Safety ",
tags: " fall,night ",
body: " Residents need clear walkways. ",
});
expect(result).toEqual({
success: true,
input: {
...validKnowledgeInput,
title: "Fall prevention guidance",
category: "Safety",
tags: "fall,night",
body: "Residents need clear walkways.",
},
});
});
it.each([
{ name: "non-object payload", value: null, reason: "请求数据格式无效" },
{ name: "unknown scope", value: { ...validKnowledgeInput, scope: "facility" }, reason: "知识库范围无效" },
{ name: "unknown status", value: { ...validKnowledgeInput, status: "archived" }, reason: "知识库状态无效" },
{ name: "blank title", value: { ...validKnowledgeInput, title: " " }, reason: "知识标题不能为空" },
{ name: "blank body", value: { ...validKnowledgeInput, body: "\n\t" }, reason: "知识内容不能为空" },
])("rejects $name", ({ value, reason }) => {
expect(validateKnowledgeEntryInput(value)).toEqual({ success: false, reason });
});
});
describe("validateElderAiAnalysisOutput", () => {
it("accepts valid model output, coerces numeric confidence strings, and clamps the public score", () => {
const result = validateElderAiAnalysisOutput({
...validAnalysisOutput,
confidence: "1.25",
});
expect(result).toEqual({
...validAnalysisOutput,
confidence: 1,
});
});
it.each([
{
name: "unknown risk level",
value: { ...validAnalysisOutput, overallRiskLevel: "urgent" },
},
{
name: "finding with invalid severity",
value: {
...validAnalysisOutput,
keyFindings: [{ ...validFinding, severity: "debug" }],
},
},
{
name: "recommendation with invalid priority",
value: {
...validAnalysisOutput,
recommendations: [{ ...validRecommendation, priority: "soon" }],
},
},
{
name: "mixed citation id array",
value: {
...validAnalysisOutput,
keyFindings: [{ ...validFinding, citationIds: ["resident-1", 42] }],
},
},
{
name: "invalid citation source",
value: {
...validAnalysisOutput,
citations: [{ ...validCitation, sourceType: "web" }],
},
},
{
name: "unexpected model provider",
value: {
...validAnalysisOutput,
modelSummary: { ...validAnalysisOutput.modelSummary, provider: "anthropic" },
},
},
{
name: "unexpected knowledge retrieval mode",
value: {
...validAnalysisOutput,
modelSummary: { ...validAnalysisOutput.modelSummary, knowledgeRetrieval: "embedding" },
},
},
{
name: "non-finite confidence",
value: { ...validAnalysisOutput, confidence: "not-a-number" },
},
])("rejects $name", ({ value }) => {
expect(validateElderAiAnalysisOutput(value)).toBeNull();
});
});
describe("parseDataScopes", () => {
it("keeps only recognized scope strings in caller order", () => {
expect(parseDataScopes(["elder", "health", "unknown", 7, "knowledge", "elder"])).toEqual([
"elder",
"health",
"knowledge",
"elder",
]);
});
it.each([undefined, null, "elder", { scopes: ["elder"] }])("returns an empty list for non-array input %#", (value) => {
expect(parseDataScopes(value)).toEqual([]);
});
});
});