| import { test } from "node:test"; |
| import assert from "node:assert"; |
| import { generateFollowUps, classifyRisk, draftEmail, generateActionPlan } from "../followup/executor"; |
| import { InteractionCapture, TerritoryAccount, FollowUpAction } from "@/types"; |
|
|
| const account: TerritoryAccount = { |
| id: "acc-1", hcpName: "Jane Smith", specialty: "Oncology", territory: "West-1", |
| funnelState: "barrier_identified", barrier: "formulary", |
| eligiblePatientOpportunity: 0.7, unmetInfoNeed: 0.6, accessProbability: 0.4, |
| expectedResponsiveness: 0.5, evidenceConfidence: 0.7, fieldTimeRequired: 1, |
| operationalFriction: 0.3, uncertaintyRisk: 0.2, priorityScore: 0.65, |
| reasonCodes: [], recommendedAction: { action: "share formulary info", rationale: "r", fieldRole: "rep", permittedChannel: "email", approvedContentId: "APR-001", estimatedTimeMin: 30, expectedOutcome: "info delivered", confidenceLevel: 0.8, evidenceThatWouldDisprove: "x", autonomyClass: 2 }, |
| }; |
|
|
| const capture: InteractionCapture = { |
| accountId: "acc-1", hcpName: "Jane Smith", rawInput: "met with HCP", |
| knowledgeState: "familiar with on-label data", primaryBarrier: "formulary", |
| secondaryBarrier: "reimbursement", requestedFollowUp: "please send approved formulary materials", |
| nextBestAction: "share approved formulary status", confidence: 0.85, |
| evidenceSource: "in-person", humanConfirmationRequired: false, structuredAt: "2025-01-15T10:00:00Z", |
| }; |
|
|
| test("generateFollowUps produces actions with consistent risk/behavior", () => { |
| const actions = generateFollowUps(capture, account); |
| assert.ok(actions.length > 0, "should produce actions"); |
| for (const a of actions) { |
| const map = { low: "auto_record", moderate: "draft_and_approve", high: "route_to_compliance", prohibited: "block_and_document" } as const; |
| assert.equal(a.systemBehavior, map[a.riskLevel], `behavior matches risk for ${a.type}`); |
| } |
| console.log("types:", actions.map(a => a.type).join(", ")); |
| console.log("risks:", actions.map(a => a.riskLevel).join(", ")); |
| }); |
|
|
| test("draftEmail is fair-balanced and references approved material", () => { |
| const email = draftEmail(capture, account); |
| assert.match(email, /APR-001/); |
| assert.match(email, /fair-balanced/i); |
| assert.doesNotMatch(email, /off-label/i); |
| assert.doesNotMatch(email, /guarantee/i); |
| }); |
|
|
| test("generateActionPlan includes prioritized steps and compliance reminders", () => { |
| const plan = generateActionPlan(capture, account); |
| assert.match(plan, /Prioritized Next Steps/); |
| assert.match(plan, /Compliance Reminders/); |
| assert.match(plan, /formulary/i); |
| }); |
|
|
| test("prohibited content is blocked and documented", () => { |
| const bad = { ...capture, requestedFollowUp: "please send off-label use information" }; |
| const actions = generateFollowUps(bad, account); |
| const blocked = actions.find(a => a.systemBehavior === "block_and_document"); |
| assert.ok(blocked, "should have a blocked action"); |
| assert.equal(blocked!.riskLevel, "prohibited"); |
| }); |
|
|
| test("classifyRisk escalates high-risk content on low-baseline type", () => { |
| const action: FollowUpAction = { |
| type: "crm_note", description: "note about adverse event report", |
| riskLevel: "low", systemBehavior: "auto_record", |
| }; |
| assert.equal(classifyRisk(action), "high"); |
| }); |
|
|
| test("action plan with instructional off-label reminders is NOT prohibited", () => { |
| const actions = generateFollowUps(capture, account); |
| const plan = actions.find(a => a.type === "action_plan"); |
| assert.ok(plan, "action plan should exist"); |
| assert.notEqual(plan!.riskLevel, "prohibited", "instructional off-label mention must not be blocked"); |
| assert.match(plan!.draftContent ?? "", /off-label/i, "plan should contain the compliance reminder"); |
| }); |
|
|
| test("explicit intent to send off-label is blocked even in a crm_note", () => { |
| const action: FollowUpAction = { |
| type: "crm_note", description: "send off-label information to HCP", |
| riskLevel: "low", systemBehavior: "auto_record", |
| }; |
| assert.equal(classifyRisk(action), "prohibited"); |
| }); |
|
|