249 lines
8.5 KiB
JavaScript
249 lines
8.5 KiB
JavaScript
/**
|
|
* 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);
|
|
});
|
|
});
|