chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
/**
|
||||
* Unit tests for batch dashboard display logic.
|
||||
*
|
||||
* Tests the effectiveStatus() and progress calculation functions that
|
||||
* determine how batch status badges and progress bars are rendered in
|
||||
* BatchListTab.tsx and BatchDetailModal.tsx.
|
||||
*
|
||||
* These functions are pure and depend on no external modules, so they
|
||||
* are duplicated here to allow isolated testing without React/jsdom.
|
||||
*/
|
||||
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
// ── Pure functions mirroring BatchListTab.tsx / BatchDetailModal.tsx ──────
|
||||
|
||||
/**
|
||||
* Returns a composite status key that reflects whether partial failures
|
||||
* occurred within a batch. Mirrors effectiveStatus() in the UI components.
|
||||
*/
|
||||
function effectiveStatus(batch) {
|
||||
const hasFailed = (batch.requestCountsFailed ?? 0) > 0;
|
||||
if (!hasFailed) return batch.status;
|
||||
const map = {
|
||||
completed: "completed_with_failures",
|
||||
in_progress: "in_progress_with_failures",
|
||||
finalizing: "finalizing_with_failures",
|
||||
cancelled: "cancelled_with_failures",
|
||||
};
|
||||
return map[batch.status] ?? batch.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes progress bar segment percentages (green = done, red = failed).
|
||||
* Mirrors the inline calculations in BatchListTab and BatchDetailModal.
|
||||
*/
|
||||
function progressPcts(batch) {
|
||||
const total = batch.requestCountsTotal || 0;
|
||||
const completed = batch.requestCountsCompleted || 0;
|
||||
const failed = batch.requestCountsFailed || 0;
|
||||
const donePct = total > 0 ? (completed / total) * 100 : 0;
|
||||
const failedPct = total > 0 ? (failed / total) * 100 : 0;
|
||||
return { donePct, failedPct, total, completed, failed };
|
||||
}
|
||||
|
||||
// ── effectiveStatus tests ─────────────────────────────────────────────────
|
||||
|
||||
describe("effectiveStatus — no failures", () => {
|
||||
it("returns 'completed' when requestCountsFailed is 0", () => {
|
||||
assert.equal(effectiveStatus({ status: "completed", requestCountsFailed: 0 }), "completed");
|
||||
});
|
||||
|
||||
it("returns 'in_progress' when requestCountsFailed is 0", () => {
|
||||
assert.equal(effectiveStatus({ status: "in_progress", requestCountsFailed: 0 }), "in_progress");
|
||||
});
|
||||
|
||||
it("returns 'finalizing' when requestCountsFailed is 0", () => {
|
||||
assert.equal(effectiveStatus({ status: "finalizing", requestCountsFailed: 0 }), "finalizing");
|
||||
});
|
||||
|
||||
it("returns 'cancelled' when requestCountsFailed is 0", () => {
|
||||
assert.equal(effectiveStatus({ status: "cancelled", requestCountsFailed: 0 }), "cancelled");
|
||||
});
|
||||
|
||||
it("returns 'failed' unchanged (already a failure state)", () => {
|
||||
assert.equal(effectiveStatus({ status: "failed", requestCountsFailed: 0 }), "failed");
|
||||
});
|
||||
|
||||
it("returns 'validating' unchanged", () => {
|
||||
assert.equal(effectiveStatus({ status: "validating", requestCountsFailed: 0 }), "validating");
|
||||
});
|
||||
|
||||
it("returns 'expired' unchanged", () => {
|
||||
assert.equal(effectiveStatus({ status: "expired", requestCountsFailed: 0 }), "expired");
|
||||
});
|
||||
|
||||
it("returns 'cancelling' unchanged", () => {
|
||||
assert.equal(effectiveStatus({ status: "cancelling", requestCountsFailed: 0 }), "cancelling");
|
||||
});
|
||||
});
|
||||
|
||||
describe("effectiveStatus — with failures", () => {
|
||||
it("completed + failures → completed_with_failures", () => {
|
||||
assert.equal(
|
||||
effectiveStatus({ status: "completed", requestCountsFailed: 3 }),
|
||||
"completed_with_failures"
|
||||
);
|
||||
});
|
||||
|
||||
it("in_progress + failures → in_progress_with_failures", () => {
|
||||
assert.equal(
|
||||
effectiveStatus({ status: "in_progress", requestCountsFailed: 1 }),
|
||||
"in_progress_with_failures"
|
||||
);
|
||||
});
|
||||
|
||||
it("finalizing + failures → finalizing_with_failures", () => {
|
||||
assert.equal(
|
||||
effectiveStatus({ status: "finalizing", requestCountsFailed: 2 }),
|
||||
"finalizing_with_failures"
|
||||
);
|
||||
});
|
||||
|
||||
it("cancelled + failures → cancelled_with_failures", () => {
|
||||
assert.equal(
|
||||
effectiveStatus({ status: "cancelled", requestCountsFailed: 5 }),
|
||||
"cancelled_with_failures"
|
||||
);
|
||||
});
|
||||
|
||||
it("failed + failures → failed (already a terminal error, no extra suffix)", () => {
|
||||
// 'failed' means the whole batch failed, not partial — no mapping needed
|
||||
assert.equal(effectiveStatus({ status: "failed", requestCountsFailed: 10 }), "failed");
|
||||
});
|
||||
|
||||
it("expired + failures → expired (no mapping defined)", () => {
|
||||
assert.equal(effectiveStatus({ status: "expired", requestCountsFailed: 1 }), "expired");
|
||||
});
|
||||
|
||||
it("validating + failures → validating (no mapping defined)", () => {
|
||||
assert.equal(effectiveStatus({ status: "validating", requestCountsFailed: 1 }), "validating");
|
||||
});
|
||||
|
||||
it("cancelling + failures → cancelling (no mapping defined)", () => {
|
||||
assert.equal(effectiveStatus({ status: "cancelling", requestCountsFailed: 2 }), "cancelling");
|
||||
});
|
||||
});
|
||||
|
||||
describe("effectiveStatus — edge cases", () => {
|
||||
it("treats null requestCountsFailed as 0", () => {
|
||||
assert.equal(effectiveStatus({ status: "completed", requestCountsFailed: null }), "completed");
|
||||
});
|
||||
|
||||
it("treats undefined requestCountsFailed as 0", () => {
|
||||
assert.equal(effectiveStatus({ status: "completed" }), "completed");
|
||||
});
|
||||
|
||||
it("treats negative failed count as 0 (> 0 check is false for negatives)", () => {
|
||||
// Negative counts should not happen in practice.
|
||||
// The check is `> 0`, so -1 does not trigger the failure mapping.
|
||||
assert.equal(effectiveStatus({ status: "completed", requestCountsFailed: -1 }), "completed");
|
||||
});
|
||||
});
|
||||
|
||||
// ── Progress percentage tests ─────────────────────────────────────────────
|
||||
|
||||
describe("progressPcts — zero total", () => {
|
||||
it("returns 0 for both pcts when total is 0", () => {
|
||||
const { donePct, failedPct } = progressPcts({
|
||||
requestCountsTotal: 0,
|
||||
requestCountsCompleted: 0,
|
||||
requestCountsFailed: 0,
|
||||
});
|
||||
assert.equal(donePct, 0);
|
||||
assert.equal(failedPct, 0);
|
||||
});
|
||||
|
||||
it("returns 0 for both pcts when total is missing", () => {
|
||||
const { donePct, failedPct } = progressPcts({});
|
||||
assert.equal(donePct, 0);
|
||||
assert.equal(failedPct, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("progressPcts — normal cases", () => {
|
||||
it("all completed, no failures → donePct=100, failedPct=0", () => {
|
||||
const { donePct, failedPct } = progressPcts({
|
||||
requestCountsTotal: 10,
|
||||
requestCountsCompleted: 10,
|
||||
requestCountsFailed: 0,
|
||||
});
|
||||
assert.equal(donePct, 100);
|
||||
assert.equal(failedPct, 0);
|
||||
});
|
||||
|
||||
it("all failed, none completed → donePct=0, failedPct=100", () => {
|
||||
const { donePct, failedPct } = progressPcts({
|
||||
requestCountsTotal: 4,
|
||||
requestCountsCompleted: 0,
|
||||
requestCountsFailed: 4,
|
||||
});
|
||||
assert.equal(donePct, 0);
|
||||
assert.equal(failedPct, 100);
|
||||
});
|
||||
|
||||
it("mixed: 6 done, 2 failed of 10 total", () => {
|
||||
const { donePct, failedPct } = progressPcts({
|
||||
requestCountsTotal: 10,
|
||||
requestCountsCompleted: 6,
|
||||
requestCountsFailed: 2,
|
||||
});
|
||||
assert.equal(donePct, 60);
|
||||
assert.equal(failedPct, 20);
|
||||
});
|
||||
|
||||
it("done + failed < total (still in progress)", () => {
|
||||
const { donePct, failedPct } = progressPcts({
|
||||
requestCountsTotal: 100,
|
||||
requestCountsCompleted: 30,
|
||||
requestCountsFailed: 10,
|
||||
});
|
||||
assert.equal(donePct, 30);
|
||||
assert.equal(failedPct, 10);
|
||||
});
|
||||
|
||||
it("single item done", () => {
|
||||
const { donePct, failedPct } = progressPcts({
|
||||
requestCountsTotal: 1,
|
||||
requestCountsCompleted: 1,
|
||||
requestCountsFailed: 0,
|
||||
});
|
||||
assert.equal(donePct, 100);
|
||||
assert.equal(failedPct, 0);
|
||||
});
|
||||
|
||||
it("single item failed", () => {
|
||||
const { donePct, failedPct } = progressPcts({
|
||||
requestCountsTotal: 1,
|
||||
requestCountsCompleted: 0,
|
||||
requestCountsFailed: 1,
|
||||
});
|
||||
assert.equal(donePct, 0);
|
||||
assert.equal(failedPct, 100);
|
||||
});
|
||||
});
|
||||
|
||||
describe("progressPcts — combined progress bar width", () => {
|
||||
it("combined pct (done + failed) equals overall processed fraction", () => {
|
||||
const { donePct, failedPct } = progressPcts({
|
||||
requestCountsTotal: 10,
|
||||
requestCountsCompleted: 3,
|
||||
requestCountsFailed: 4,
|
||||
});
|
||||
const combinedPct = Math.round(donePct + failedPct);
|
||||
assert.equal(combinedPct, 70); // (3+4)/10 = 70%
|
||||
});
|
||||
|
||||
it("combined pct stays ≤ 100 when all processed", () => {
|
||||
const { donePct, failedPct } = progressPcts({
|
||||
requestCountsTotal: 5,
|
||||
requestCountsCompleted: 3,
|
||||
requestCountsFailed: 2,
|
||||
});
|
||||
const combinedPct = donePct + failedPct;
|
||||
assert.ok(combinedPct <= 100, `Combined pct ${combinedPct} should not exceed 100`);
|
||||
assert.equal(combinedPct, 100);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user