feat(sp25): extend useTailStore with acks + ta1_acks slices
The Acks page needs the same live-tail triplet as Claims/Remittances/ Activity. Both Ack and Ta1Ack have stable numeric ids from the database row, so they get the keyed-by-id treatment (first write wins, FIFO-capped at TAIL_CAP) — same shape as the claims and remittances slices, not the append-only activity array. Adds addAck, addTa1Ack, ackOrder, ta1AckOrder, and the matching reset cases for the new 'acks' / 'ta1_acks' TailResource kinds. evictOldest is now generic over T so the same helper covers the two new keyed-by-id slices without duplication.
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
// state shape.
|
||||
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import type { Activity, Claim, Remittance } from "@/types";
|
||||
import type { Ack, Activity, Claim, Remittance, Ta1Ack } from "@/types";
|
||||
import { TAIL_CAP, useTailStore } from "./tail-store";
|
||||
|
||||
function makeClaim(id: string, overrides: Partial<Claim> = {}): Claim {
|
||||
@@ -47,6 +47,36 @@ function makeActivity(id: string, overrides: Partial<Activity> = {}): Activity {
|
||||
};
|
||||
}
|
||||
|
||||
function makeAck(id: number, overrides: Partial<Ack> = {}): Ack {
|
||||
return {
|
||||
id,
|
||||
sourceBatchId: `BATCH-${id}`,
|
||||
acceptedCount: 1,
|
||||
rejectedCount: 0,
|
||||
receivedCount: 1,
|
||||
ackCode: "A",
|
||||
parsedAt: "2026-07-02T12:00:00Z",
|
||||
patientControlNumber: `PCN-${id}`,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function makeTa1Ack(id: number, overrides: Partial<Ta1Ack> = {}): Ta1Ack {
|
||||
return {
|
||||
id,
|
||||
controlNumber: `0000000${id}`,
|
||||
ackCode: "A",
|
||||
noteCode: "000",
|
||||
interchangeDate: "2026-07-02",
|
||||
interchangeTime: "1200",
|
||||
senderId: "SENDER",
|
||||
receiverId: "RECEIVER",
|
||||
sourceBatchId: `TA1-BATCH-${id}`,
|
||||
parsedAt: "2026-07-02T12: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
|
||||
@@ -55,6 +85,8 @@ describe("useTailStore", () => {
|
||||
useTailStore.getState().reset("claims");
|
||||
useTailStore.getState().reset("remittances");
|
||||
useTailStore.getState().reset("activity");
|
||||
useTailStore.getState().reset("acks");
|
||||
useTailStore.getState().reset("ta1_acks");
|
||||
});
|
||||
|
||||
it("test_add_claim_adds_new_claim_keyed_by_id", () => {
|
||||
@@ -128,4 +160,71 @@ describe("useTailStore", () => {
|
||||
const lastKey = `CLM-${(N - 1).toString().padStart(6, "0")}`;
|
||||
expect(claims[lastKey]).toBeDefined();
|
||||
}, 60_000);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// SP25: acks + ta1_acks slices mirror the claims/remittances key-by-id
|
||||
// pattern (both have a stable `id` from the database row).
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
it("test_add_ack_adds_new_ack_keyed_by_id", () => {
|
||||
const { addAck } = useTailStore.getState();
|
||||
addAck(makeAck(1));
|
||||
addAck(makeAck(2));
|
||||
const { acks } = useTailStore.getState();
|
||||
expect(Object.keys(acks)).toHaveLength(2);
|
||||
expect(acks["1"]?.sourceBatchId).toBe("BATCH-1");
|
||||
expect(acks["2"]?.sourceBatchId).toBe("BATCH-2");
|
||||
});
|
||||
|
||||
it("test_add_ack_with_duplicate_id_is_noop", () => {
|
||||
const { addAck } = useTailStore.getState();
|
||||
addAck(makeAck(1, { sourceBatchId: "BATCH-original" }));
|
||||
addAck(makeAck(1, { sourceBatchId: "BATCH-replay" }));
|
||||
const { acks } = useTailStore.getState();
|
||||
expect(Object.keys(acks)).toHaveLength(1);
|
||||
// First write wins; snapshot replay must not trample the canonical row.
|
||||
expect(acks["1"]?.sourceBatchId).toBe("BATCH-original");
|
||||
});
|
||||
|
||||
it("test_reset_acks_clears_only_acks_slice", () => {
|
||||
const { addAck, addClaim, reset } = useTailStore.getState();
|
||||
addAck(makeAck(1));
|
||||
addClaim(makeClaim("CLM-1"));
|
||||
|
||||
reset("acks");
|
||||
const s = useTailStore.getState();
|
||||
expect(Object.keys(s.acks)).toHaveLength(0);
|
||||
// The other slices are untouched.
|
||||
expect(Object.keys(s.claims)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("test_add_ta1_ack_adds_new_ta1_ack_keyed_by_id", () => {
|
||||
const { addTa1Ack } = useTailStore.getState();
|
||||
addTa1Ack(makeTa1Ack(1));
|
||||
addTa1Ack(makeTa1Ack(2));
|
||||
const { ta1Acks } = useTailStore.getState();
|
||||
expect(Object.keys(ta1Acks)).toHaveLength(2);
|
||||
expect(ta1Acks["1"]?.controlNumber).toBe("00000001");
|
||||
expect(ta1Acks["2"]?.controlNumber).toBe("00000002");
|
||||
});
|
||||
|
||||
it("test_add_ta1_ack_with_duplicate_id_is_noop", () => {
|
||||
const { addTa1Ack } = useTailStore.getState();
|
||||
addTa1Ack(makeTa1Ack(7, { senderId: "ORIGINAL" }));
|
||||
addTa1Ack(makeTa1Ack(7, { senderId: "REPLAY" }));
|
||||
const { ta1Acks } = useTailStore.getState();
|
||||
expect(Object.keys(ta1Acks)).toHaveLength(1);
|
||||
expect(ta1Acks["7"]?.senderId).toBe("ORIGINAL");
|
||||
});
|
||||
|
||||
it("test_reset_ta1_acks_clears_only_ta1_acks_slice", () => {
|
||||
const { addTa1Ack, addAck, reset } = useTailStore.getState();
|
||||
addTa1Ack(makeTa1Ack(1));
|
||||
addAck(makeAck(1));
|
||||
|
||||
reset("ta1_acks");
|
||||
const s = useTailStore.getState();
|
||||
expect(Object.keys(s.ta1Acks)).toHaveLength(0);
|
||||
expect(Object.keys(s.acks)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user