// @vitest-environment node // `useTailStore` is a pure zustand store with no DOM / React dependencies, // so a node environment is fine. We import `getState()` and `setState` // from the store directly (no React render) and assert on the resulting // state shape. import { beforeEach, describe, expect, it } from "vitest"; import type { Activity, Claim, Remittance } from "@/types"; import { TAIL_CAP, useTailStore } from "./tail-store"; function makeClaim(id: string, overrides: Partial = {}): Claim { return { id, patientName: `Patient ${id}`, providerNpi: "1234567890", payerName: "Medicaid", cptCode: "99213", billedAmount: 100, receivedAmount: 0, status: "submitted", submissionDate: "2026-06-20T00:00:00Z", ...overrides, }; } function makeRemittance(id: string, overrides: Partial = {}): Remittance { return { id, claimId: `CLM-${id}`, payerName: "Medicaid", paidAmount: 100, adjustmentAmount: 0, receivedDate: "2026-06-20", checkNumber: id, status: "received", ...overrides, }; } function makeActivity(id: string, overrides: Partial = {}): Activity { return { id, kind: "claim_submitted", message: `Event ${id}`, timestamp: "2026-06-20T00:00:00Z", ...overrides, }; } describe("useTailStore", () => { // Each test starts from a known-empty state. The store is module-level // (singleton), so we use `reset()` to clear between cases — that's also // what production code calls on stream open. beforeEach(() => { useTailStore.getState().reset("claims"); useTailStore.getState().reset("remittances"); useTailStore.getState().reset("activity"); }); it("test_add_claim_adds_new_claim_keyed_by_id", () => { const { addClaim } = useTailStore.getState(); addClaim(makeClaim("CLM-1")); addClaim(makeClaim("CLM-2")); const { claims } = useTailStore.getState(); expect(Object.keys(claims)).toHaveLength(2); expect(claims["CLM-1"]?.id).toBe("CLM-1"); expect(claims["CLM-2"]?.id).toBe("CLM-2"); }); it("test_add_claim_with_duplicate_id_is_noop", () => { const { addClaim } = useTailStore.getState(); addClaim(makeClaim("CLM-1", { patientName: "Original" })); addClaim(makeClaim("CLM-1", { patientName: "Updated" })); const { claims } = useTailStore.getState(); expect(Object.keys(claims)).toHaveLength(1); // First write wins; duplicates are silently dropped so the snapshot // replay on reconnect doesn't trample the canonical row. expect(claims["CLM-1"]?.patientName).toBe("Original"); }); it("test_reset_claims_clears_only_claims_slice", () => { const { addClaim, addRemittance, addActivity, reset } = useTailStore.getState(); addClaim(makeClaim("CLM-1")); addRemittance(makeRemittance("RMT-1")); addActivity(makeActivity("ACT-1")); reset("claims"); const s = useTailStore.getState(); expect(Object.keys(s.claims)).toHaveLength(0); expect(Object.keys(s.remittances)).toHaveLength(1); expect(s.activity).toHaveLength(1); }); it("test_add_activity_appends", () => { const { addActivity } = useTailStore.getState(); addActivity(makeActivity("ACT-1", { message: "first" })); addActivity(makeActivity("ACT-2", { message: "second" })); addActivity(makeActivity("ACT-3", { message: "third" })); const { activity } = useTailStore.getState(); expect(activity).toHaveLength(3); // Activity has no stable id; it lives in an array. Insertion order is // the only ordering signal. expect(activity.map((a) => a.message)).toEqual(["first", "second", "third"]); }); it("test_fifo_cap_evicts_oldest_when_over_10000", () => { // Insert 5 over the cap so the eviction policy must actually run. // The plan calls for evicting the oldest 100 (or enough to be back at // the cap) — we just assert: size == TAIL_CAP and the very-first // items are gone. // // 10 005 individual `addClaim` calls is intentionally a stress test // of the eviction path: each call rebuilds the dict + order array // so the wall-clock cost is O(N^2) in the number of items ever // inserted. Bump the per-test timeout from the default 5s to 60s // so this case can complete in CI on slow runners. const N = TAIL_CAP + 5; const { addClaim } = useTailStore.getState(); for (let i = 0; i < N; i++) { addClaim(makeClaim(`CLM-${i.toString().padStart(6, "0")}`)); } const { claims } = useTailStore.getState(); expect(Object.keys(claims)).toHaveLength(TAIL_CAP); // The five oldest (CLM-000000 .. CLM-000004) must be gone. expect(claims["CLM-000000"]).toBeUndefined(); expect(claims["CLM-000004"]).toBeUndefined(); // The most recent (CLM-0010004) must be present. const lastKey = `CLM-${(N - 1).toString().padStart(6, "0")}`; expect(claims[lastKey]).toBeDefined(); }, 60_000); });