import { describe, expect, it } from "vitest"; import { runCheck, checkFirstReply, checkPersonalization, checkContradiction, checkTimeInStage, checkReminders, checkFrequency, checkWorkflows, renderedGreetingName } from "./check"; import { EXAMPLE_NOW as NOW, exampleConfig as cfg, exampleSnapshot as base } from "./example"; import { applyScenario } from "./scenarios"; import type { Contact, Message, Snapshot } from "./types"; // Small builders to edit the example without touching it. const clone = (s: Snapshot): Snapshot => structuredClone(s); const editContact = (s: Snapshot, id: string, patch: Partial) => { const x = clone(s); x.contacts = x.contacts.map((c) => (c.id === id ? { ...c, ...patch } : c)); return x; }; const without = (s: Snapshot, pred: (m: Message) => boolean) => ({ ...clone(s), messages: s.messages.filter((m) => !pred(m)) }); const kinds = (fs: { kind: string; contactId?: string }[], contactId?: string) => fs.filter((f) => !contactId || f.contactId === contactId).map((f) => f.kind); describe("the example run", () => { const r = runCheck(base, cfg, NOW); it("reads every source and reaches a verdict", () => { expect(r.collectionProblems).toEqual([]); expect(r.cannotConclude).toEqual([]); expect(r.verdict).toBe("exceptions_found"); }); it("finds the three primary cases and nothing else in the foreground", () => { const primary = r.findings.filter((f) => f.tier === "primary").map((f) => `${f.kind}:${f.contactId}`); expect(primary.sort()).toEqual([ "contradictory_followup:c_priya", "no_qualifying_reply:c_dana", "personalization_defect:c_marcus", ]); }); it("counts exceptions and leads separately", () => { expect(r.counts.exceptions).toBe(r.findings.length); expect(r.counts.contactsConcerned).toBeLessThan(r.counts.exceptions); expect(r.counts.contactsSetAside).toBe(new Set(r.setAside.map((x) => x.contactId)).size); expect(r.counts.contactsSetAside).toBeLessThan(r.setAside.length); // Sam is set aside by two checks, counted once }); it("sets the agreed wait aside instead of flagging it", () => { expect(r.setAside.find((x) => x.contactId === "c_renee")?.reason).toBe("waiting_agreed"); expect(kinds(r.findings, "c_renee")).toEqual([]); }); }); describe("check 1: no qualifying reply", () => { it("flags the patio inquiry with no reply after the deadline", () => { const f = checkFirstReply(base, cfg, NOW).findings.find((x) => x.contactId === "c_dana"); expect(f?.detail.minutesPastDue).toBe(Math.round((Date.parse(base.collectedAt) - Date.parse("2026-09-22T10:12:31-05:00")) / 60000)); }); it("does not count a newsletter as a reply (witness: a real reply clears it)", () => { expect(kinds(checkFirstReply(base, cfg, NOW).findings)).toContain("no_qualifying_reply"); const answered = clone(base); answered.messages.push({ id: "m_x", messageType: "TYPE_SMS", contactId: "c_dana", conversationId: "cv", dateAdded: "2026-09-22T09:20:00-05:00", direction: "outbound", status: "delivered", source: "workflow", body: "Thanks for reaching out about your project. When is a good time for a quick call?" }); expect(kinds(checkFirstReply(answered, cfg, NOW).findings, "c_dana")).toEqual([]); }); it("does not flag an inquiry before its deadline", () => { expect(kinds(checkFirstReply(base, cfg, "2026-09-22T10:00:00-05:00").findings, "c_dana")).toEqual([]); }); it("ignores an imported contact with a recent dateAdded (witness: a form submission makes it expected)", () => { expect(kinds(checkFirstReply(base, cfg, NOW).findings, "c_glen")).toEqual([]); const s = clone(base); s.submissions.push({ id: "sub_glen", contactId: "c_glen", formId: "form_project_inquiry", createdAt: "2026-09-22T08:00:00-05:00" }); expect(kinds(checkFirstReply(s, cfg, NOW).findings, "c_glen")).toEqual(["no_qualifying_reply"]); }); it("expects a reply for a returning client despite an old dateAdded (witness: remove the reply)", () => { expect(kinds(checkFirstReply(base, cfg, NOW).findings, "c_keene")).toEqual([]); const s = without(base, (m) => m.contactId === "c_keene"); expect(kinds(checkFirstReply(s, cfg, NOW).findings, "c_keene")).toEqual(["no_qualifying_reply"]); }); it("ignores the warranty request form (witness: same submission on the inquiry form)", () => { expect(kinds(checkFirstReply(base, cfg, NOW).findings, "c_tyler")).toEqual([]); const s = clone(base); s.submissions = s.submissions.map((x) => (x.id === "sub_tyler" ? { ...x, formId: "form_project_inquiry" } : x)); expect(kinds(checkFirstReply(s, cfg, NOW).findings, "c_tyler")).toEqual(["no_qualifying_reply"]); }); it("counts a human phone call as a reply (witness: remove the call)", () => { expect(kinds(checkFirstReply(base, cfg, NOW).findings, "c_alicia")).toEqual([]); const s = without(base, (m) => m.contactId === "c_alicia" && m.messageType === "TYPE_CALL"); expect(kinds(checkFirstReply(s, cfg, NOW).findings, "c_alicia")).toEqual(["no_qualifying_reply"]); }); it("sets aside a contact who allows no channel (witness: allow SMS)", () => { const r = checkFirstReply(base, cfg, NOW); expect(r.setAside.find((x) => x.contactId === "c_wes")?.reason).toBe("no_authorized_channel"); const s = editContact(base, "c_wes", { dnd: false }); expect(kinds(checkFirstReply(s, cfg, NOW).findings, "c_wes")).toEqual(["no_qualifying_reply"]); }); it("does not count a reply on a channel the contact blocked", () => { const s = editContact(base, "c_keene", { dndSettings: { SMS: { status: "active" } } }); expect(kinds(checkFirstReply(s, cfg, NOW).findings, "c_keene")).toEqual(["no_qualifying_reply"]); }); it("does not count a failed send as a reply", () => { const s = clone(base); s.messages = s.messages.map((m) => (m.contactId === "c_keene" ? { ...m, status: "undelivered" } : m)); const f = checkFirstReply(s, cfg, NOW).findings.find((x) => x.contactId === "c_keene"); expect(f?.detail.failedAttempts).toBe(1); }); it("sets aside a wait the contact asked for (witness: hold in the past)", () => { const held = editContact(base, "c_dana", { customFields: [{ id: cfg.holdUntilFieldId, value: "2026-09-29T09:00:00-05:00" }] }); expect(checkFirstReply(held, cfg, NOW).setAside.find((x) => x.contactId === "c_dana")?.reason).toBe("waiting_agreed"); const expired = editContact(base, "c_dana", { customFields: [{ id: cfg.holdUntilFieldId, value: "2026-09-20T09:00:00-05:00" }] }); expect(kinds(checkFirstReply(expired, cfg, NOW).findings, "c_dana")).toEqual(["no_qualifying_reply"]); }); it("refuses to conclude when emails were exported without channel=Email (witness: full export concludes)", () => { const r = checkFirstReply(applyScenario(base, "no_email_channel"), cfg, NOW); expect(kinds(r.findings, "c_dana")).toEqual([]); expect(r.cannot.find((x) => x.contactId === "c_dana")?.missing).toEqual(["messages.email"]); }); it("concludes without email when the contact blocked email", () => { const s = editContact(applyScenario(base, "no_email_channel"), "c_dana", { dndSettings: { Email: { status: "active" } } }); expect(kinds(checkFirstReply(s, cfg, NOW).findings, "c_dana")).toEqual(["no_qualifying_reply"]); }); }); describe("check 2: personalization", () => { it("reads the greeting name", () => { expect(renderedGreetingName("Hi , your visit", "Hi ")).toBe(""); expect(renderedGreetingName("Hi Priya, your visit", "Hi ")).toBe("Priya"); expect(renderedGreetingName("Hello, your visit", "Hi ")).toBeNull(); }); it("flags the blank greeting actually sent (witness: the same template with a name)", () => { const r = checkPersonalization(base, cfg, NOW); expect(kinds(r.findings, "c_marcus")).toEqual(["personalization_defect"]); const s = clone(base); s.messages = s.messages.map((m) => (m.body?.startsWith("Hi , your home") ? { ...m, body: m.body.replace("Hi ,", "Hi Marcus,") } : m)); expect(kinds(checkPersonalization(s, cfg, NOW).findings, "c_marcus")).toEqual([]); }); it("never treats a missing body as a clean message", () => { const s = clone(base); s.messages = s.messages.map((m) => (m.body?.startsWith("Hi , your home") ? { ...m, body: undefined } : m)); const r = checkPersonalization(s, cfg, NOW); expect(kinds(r.findings, "c_marcus")).toEqual([]); expect(r.cannot.some((x) => x.contactId === "c_marcus")).toBe(true); }); it("lists the risk before the send (witness: add the first name)", () => { expect(kinds(checkPersonalization(base, cfg, NOW).findings, "c_ortiz")).toEqual(["personalization_risk"]); const s = editContact(base, "c_ortiz", { firstName: "Elena" }); expect(kinds(checkPersonalization(s, cfg, NOW).findings, "c_ortiz")).toEqual([]); }); }); describe("check 3: contradictory follow-up", () => { it("flags a booking ask sent after the consultation was booked", () => { expect(kinds(checkContradiction(base, cfg, NOW).findings)).toEqual(["contradictory_followup"]); }); it("sets aside a booking ask after a cancellation (witness: same message, appointment still confirmed)", () => { const r = checkContradiction(base, cfg, NOW); expect(r.setAside.find((x) => x.contactId === "c_sam")?.reason).toBe("appointment_cancelled"); const s = clone(base); s.events = s.events.map((e) => (e.id === "ev_sam" ? { ...e, appointmentStatus: "confirmed", startTime: "2026-09-25T10:00:00-05:00" } : e)); expect(kinds(checkContradiction(s, cfg, NOW).findings, "c_sam")).toEqual(["contradictory_followup"]); }); it("sets aside a cancelled appointment that was still in the future", () => { const s = clone(base); s.events = s.events.map((e) => (e.id === "ev_sam" ? { ...e, startTime: "2026-09-25T10:00:00-05:00" } : e)); const r = checkContradiction(s, cfg, NOW); expect(kinds(r.findings, "c_sam")).toEqual([]); expect(r.setAside.find((x) => x.contactId === "c_sam")?.reason).toBe("appointment_cancelled"); }); it("does not flag a booking ask sent before the booking", () => { const s = clone(base); s.events = s.events.map((e) => (e.id === "ev_priya" ? { ...e, dateAdded: "2026-09-22T11:00:00-05:00" } : e)); expect(kinds(checkContradiction(s, cfg, NOW).findings, "c_priya")).toEqual([]); }); it("still flags a rescheduled but active appointment", () => { const s = clone(base); s.events = s.events.map((e) => (e.id === "ev_priya" ? { ...e, startTime: "2026-09-29T13:00:00-05:00", dateUpdated: "2026-09-22T09:00:00-05:00" } : e)); expect(kinds(checkContradiction(s, cfg, NOW).findings, "c_priya")).toEqual(["contradictory_followup"]); }); }); describe("secondary checks", () => { it("time in stage: flags the estimate, sets aside the agreed wait (witness: no hold)", () => { const r = checkTimeInStage(base, cfg, NOW); expect(kinds(r.findings)).toEqual(["time_in_stage"]); expect(r.findings[0].contactId).toBe("c_kevin"); const s = editContact(base, "c_renee", { customFields: [] }); expect(kinds(checkTimeInStage(s, cfg, NOW).findings, "c_renee")).toEqual(["time_in_stage"]); }); it("reminders: flags Frank, sets aside late booking and cancellation", () => { const r = checkReminders(base, cfg, NOW); expect(r.findings.filter((f) => f.kind === "no_matching_reminder").map((f) => f.contactId)).toEqual(["c_frank"]); const reasons = Object.fromEntries(r.setAside.map((x) => [x.contactId, x.reason])); expect(reasons).toMatchObject({ c_hannah: "late_booking", c_sam: "appointment_cancelled" }); }); it("reminders: witness for late booking (booked earlier, no reminder: flagged)", () => { const s = clone(base); s.events = s.events.map((e) => (e.id === "ev_hannah" ? { ...e, dateAdded: "2026-09-21T15:00:00-05:00", dateUpdated: "2026-09-21T15:00:00-05:00" } : e)); expect(checkReminders(s, cfg, NOW).findings.some((f) => f.contactId === "c_hannah")).toBe(true); }); it("reminders: a matching SMS clears Frank", () => { const s = clone(base); s.messages.push({ id: "m_r", messageType: "TYPE_SMS", contactId: "c_frank", conversationId: "cv", dateAdded: "2026-09-22T14:00:30-05:00", direction: "outbound", status: "delivered", body: "Reminder" }); expect(checkReminders(s, cfg, NOW).findings.some((f) => f.contactId === "c_frank")).toBe(false); }); it("reminders: a calendar with no native reminder is named, not guessed", () => { const f = checkReminders(base, cfg, NOW).findings.find((x) => x.kind === "no_native_reminder_configured"); expect(f?.subjectId).toBe("cal_site_measure"); }); it("frequency: counts automated messages only, and needs the email export", () => { expect(checkFrequency(base, cfg).findings.map((f) => f.contactId)).toEqual(["c_ruth"]); const partial = checkFrequency(applyScenario(base, "no_email_channel"), cfg); expect(partial.findings).toEqual([]); expect(partial.cannot[0].missing).toEqual(["messages.email"]); }); it("workflows: flags the expected workflow left in draft (witness: published)", () => { expect(checkWorkflows(base, cfg).findings.map((f) => f.detail.name)).toEqual(["Review request after completion"]); const s = clone(base); s.workflows = s.workflows.map((w) => ({ ...w, status: "published" })); expect(checkWorkflows(s, cfg).findings).toEqual([]); }); it("workflows: an unexpected draft is not a failure", () => { const s = clone(base); s.workflows.push({ id: "wf_x", name: "Holiday promo idea", status: "draft", version: 1, createdAt: NOW, updatedAt: NOW }); expect(checkWorkflows(s, cfg).findings).toHaveLength(1); }); }); describe("absences are measured to the moment the data was read", () => { const lateInquiry = (collectedAt: string) => { const s = clone(base); s.collectedAt = collectedAt; s.contacts.push({ id: "c_late", firstName: "Jo", lastName: "Late", dateAdded: "2026-09-22T14:57:00-05:00", tags: [] }); s.submissions.push({ id: "sub_late", contactId: "c_late", formId: "form_project_inquiry", createdAt: "2026-09-22T14:57:00-05:00" }); return s; }; it("a reply due at 3:57 pm is not missing in data read at 3:55 pm, even when the report shows at 4:00 pm", () => { expect(kinds(checkFirstReply(lateInquiry("2026-09-22T15:55:00-05:00"), cfg, NOW).findings, "c_late")).toEqual([]); }); it("witness: the same inquiry in data read at 4:00 pm is missing its reply", () => { expect(kinds(checkFirstReply(lateInquiry("2026-09-22T16:00:00-05:00"), cfg, NOW).findings, "c_late")).toEqual(["no_qualifying_reply"]); }); it("the delay past due is counted to the data, not to the report", () => { const f = checkFirstReply(base, cfg, NOW).findings.find((x) => x.contactId === "c_dana")!; expect(f.detail.minutesPastDue).toBe(Math.round((Date.parse(base.collectedAt) - Date.parse("2026-09-22T10:12:31-05:00")) / 60000)); }); it("a three-day-old run withholds its conclusions and knows nothing newer", () => { const r = runCheck(applyScenario(base, "stale_run"), cfg, NOW); expect(r.cannotConclude.length).toBeGreaterThan(0); expect(r.cannotConclude.every((c) => c.reason.includes("Nothing newer is known"))).toBe(true); expect(r.findings.some((f) => f.contactId === "c_dana")).toBe(false); expect(r.findings.some((f) => f.kind === "contradictory_followup")).toBe(false); }); it("witness: a fresh run withholds nothing", () => { expect(runCheck(base, cfg, NOW).cannotConclude).toEqual([]); }); }); describe("the check reports its own failures", () => { for (const id of ["no_email_channel", "cursor_expired", "no_calendar_permission", "stale_run"] as const) { it(`never says nothing to report after: ${id}`, () => { const r = runCheck(applyScenario(base, id), cfg, NOW); expect(r.verdict).toBe("incomplete"); expect(r.collectionProblems.length).toBeGreaterThan(0); }); } it("a clean account with complete collection says nothing to report", () => { const quiet: Snapshot = { ...clone(base), submissions: [], messages: [], events: [], opportunities: [], workflows: base.workflows.map((w) => ({ ...w, status: "published" })) }; expect(runCheck(quiet, cfg, NOW).verdict).toBe("nothing_to_report"); }); it("the same empty account with a missing source is incomplete, not clean", () => { const quiet: Snapshot = { ...clone(base), submissions: [], messages: [], events: [], opportunities: [], workflows: base.workflows.map((w) => ({ ...w, status: "published" })) }; expect(runCheck(applyScenario(quiet, "cursor_expired"), cfg, NOW).verdict).toBe("incomplete"); }); it("calendar permission missing: reminder and contradiction checks withhold their conclusion", () => { const r = runCheck(applyScenario(base, "no_calendar_permission"), cfg, NOW); expect(r.cannotConclude.map((c) => c.check)).toEqual(expect.arrayContaining(["no_matching_reminder", "contradictory_followup"])); expect(r.findings.some((f) => f.kind === "contradictory_followup")).toBe(false); }); }); // Counter-examples from an independent review (25/09/2026). Each made a real problem disappear before the fix. describe("counter-examples from the review", () => { const msg = (m: Partial & { contactId: string; dateAdded: string }): Message => ({ id: `m_${Math.random().toString(36).slice(2)}`, messageType: "TYPE_SMS", conversationId: "cv", direction: "outbound", status: "delivered", ...m, }); it("an unrelated automated send does not answer the inquiry (witness: the expected auto-reply does)", () => { const s = clone(base); s.messages = s.messages.map((m) => (m.contactId === "c_dana" && m.source === "bulk_actions" ? { ...m, source: "workflow" } : m)); expect(kinds(checkFirstReply(s, cfg, NOW).findings, "c_dana")).toEqual(["no_qualifying_reply"]); s.messages.push(msg({ contactId: "c_dana", dateAdded: "2026-09-22T09:13:00-05:00", source: "workflow", body: "Thanks for reaching out about your project. When is a good time for a quick call?" })); expect(kinds(checkFirstReply(s, cfg, NOW).findings, "c_dana")).toEqual([]); }); for (const status of ["failed", "undelivered", "queued", "pending", "scheduled"] as const) { it(`a ${status} reminder does not count as sent (witness: delivered clears it)`, () => { const s = clone(base); s.messages.push(msg({ contactId: "c_frank", dateAdded: "2026-09-22T14:00:30-05:00", status, body: "Reminder" })); expect(checkReminders(s, cfg, NOW).findings.some((f) => f.contactId === "c_frank")).toBe(true); s.messages = s.messages.map((m) => (m.body === "Reminder" && m.contactId === "c_frank" ? { ...m, status: "delivered" } : m)); expect(checkReminders(s, cfg, NOW).findings.some((f) => f.contactId === "c_frank")).toBe(false); }); } it("an appointment edited after the reminder was due is still checked (the record changed, not the date)", () => { const s = clone(base); s.events = s.events.map((e) => (e.id === "ev_frank" ? { ...e, title: "Home consultation (gate code 4411)", dateUpdated: "2026-09-22T15:00:00-05:00" } : e)); const f = checkReminders(s, cfg, NOW).findings.find((x) => x.contactId === "c_frank"); expect(f?.detail.recordChangedAfterDue).toBe(true); expect(checkReminders(s, cfg, NOW).setAside.some((x) => x.contactId === "c_frank")).toBe(false); }); it("a cancellation after the booking request does not excuse it (witness: cancelled before, set aside)", () => { const after = clone(base); after.events = after.events.map((e) => (e.id === "ev_priya" ? { ...e, appointmentStatus: "cancelled", dateUpdated: "2026-09-22T11:00:00-05:00" } : e)); const r = checkContradiction(after, cfg, NOW); expect(r.setAside.some((x) => x.contactId === "c_priya")).toBe(false); expect(r.cannot.some((x) => x.contactId === "c_priya")).toBe(true); const before = clone(base); before.events = before.events.map((e) => (e.id === "ev_priya" ? { ...e, appointmentStatus: "cancelled", dateUpdated: "2026-09-22T09:00:00-05:00" } : e)); expect(checkContradiction(before, cfg, NOW).setAside.find((x) => x.contactId === "c_priya")?.reason).toBe("appointment_cancelled"); }); it("an appointment later marked showed keeps the past contradiction (witness: the base case)", () => { const s = clone(base); s.events = s.events.map((e) => (e.id === "ev_priya" ? { ...e, appointmentStatus: "showed" } : e)); expect(kinds(checkContradiction(s, cfg, NOW).findings, "c_priya")).toEqual(["contradictory_followup"]); expect(kinds(checkContradiction(base, cfg, NOW).findings, "c_priya")).toEqual(["contradictory_followup"]); }); it("unreadable contacts: an agreed wait cannot be seen, so time in stage does not conclude (witness: readable)", () => { const s = clone(base); s.sources = s.sources.map((x) => (x.source === "contacts" ? { ...x, status: "forbidden" } : x)); s.contacts = []; const r = checkTimeInStage(s, cfg, NOW); expect(r.findings.some((f) => f.contactId === "c_renee")).toBe(false); expect(r.cannot.some((c) => c.contactId === "c_renee")).toBe(true); expect(checkTimeInStage(base, cfg, NOW).setAside.find((x) => x.contactId === "c_renee")?.reason).toBe("waiting_agreed"); }); it("a submission whose contact is missing is never nothing to report (witness: with its contact)", () => { const quiet: Snapshot = { ...clone(base), submissions: [], messages: [], events: [], opportunities: [], workflows: base.workflows.map((w) => ({ ...w, status: "published" })) }; quiet.submissions = [{ id: "sub_orphan", contactId: "c_ghost", formId: "form_project_inquiry", createdAt: "2026-09-22T09:00:00-05:00" }]; const r = runCheck(quiet, cfg, NOW); expect(r.verdict).toBe("incomplete"); expect(r.cannotConclude.some((c) => c.subjectId === "sub_orphan")).toBe(true); quiet.contacts.push({ id: "c_ghost", firstName: "Lee", lastName: "Ghost", dateAdded: "2026-09-22T09:00:00-05:00", tags: [] }); expect(runCheck(quiet, cfg, NOW).findings.some((f) => f.contactId === "c_ghost")).toBe(true); }); it("a reply dated after the data was read is not used, and the run says so (witness: a reply before)", () => { const s = clone(base); s.messages.push(msg({ contactId: "c_dana", dateAdded: "2026-09-22T17:00:00-05:00", source: "app", body: "Hi Dana" })); expect(kinds(checkFirstReply(s, cfg, NOW).findings, "c_dana")).toEqual(["no_qualifying_reply"]); expect(runCheck(s, cfg, NOW).collectionProblems.some((p) => p.problem === "dated_after_read")).toBe(true); s.messages = s.messages.map((m) => (m.body === "Hi Dana" ? { ...m, dateAdded: "2026-09-22T15:00:00-05:00" } : m)); expect(kinds(checkFirstReply(s, cfg, NOW).findings, "c_dana")).toEqual([]); }); it("an unanswered call is an attempt, not a reply (witness: a completed call is)", () => { const s = clone(base); s.messages.push(msg({ contactId: "c_dana", dateAdded: "2026-09-22T09:30:00-05:00", messageType: "TYPE_CALL", source: "app", status: undefined, meta: { callStatus: "no-answer" } })); expect(kinds(checkFirstReply(s, cfg, NOW).findings, "c_dana")).toEqual(["no_qualifying_reply"]); s.messages = s.messages.map((m) => (m.meta?.callStatus === "no-answer" ? { ...m, meta: { callStatus: "completed" } } : m)); expect(kinds(checkFirstReply(s, cfg, NOW).findings, "c_dana")).toEqual([]); }); it("accepts SMS as well as TYPE_SMS (to confirm on a real response)", () => { const s = clone(base); s.messages = s.messages.map((m) => (m.contactId === "c_keene" ? { ...m, messageType: "SMS" } : m)); expect(kinds(checkFirstReply(s, cfg, NOW).findings, "c_keene")).toEqual([]); }); }); describe("contacts correctly not flagged are computed, not listed by hand", () => { it("finds the import, the warranty form, the returning client and the call", async () => { const { notFlagged } = await import("./notFlagged"); const r = notFlagged(base, cfg, NOW).map((x) => `${x.kind}:${x.contactId}`).sort(); expect(r).toEqual(["added_without_inquiry:c_glen", "answered_by_call:c_alicia", "other_form:c_tyler", "returning_client_answered:c_keene"]); }); it("witness: an unanswered returning client is flagged, not listed as correctly left alone", async () => { const { notFlagged } = await import("./notFlagged"); const s = without(base, (m) => m.contactId === "c_keene"); expect(notFlagged(s, cfg, NOW).some((x) => x.contactId === "c_keene")).toBe(false); expect(kinds(checkFirstReply(s, cfg, NOW).findings, "c_keene")).toEqual(["no_qualifying_reply"]); }); });