152 lines
4.2 KiB
TypeScript
152 lines
4.2 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
OPERATOR_QUEUE_REASON,
|
|
campaignLifecycleTotals,
|
|
campaignVersionWorkItems,
|
|
operatorDeliveryStateCounts,
|
|
operatorQueueActionBlocks,
|
|
shouldRefreshOperatorVersionSummary,
|
|
type OperatorQueueFacts,
|
|
type OperatorQueuePermissions
|
|
} from "../src/features/operator/operatorQueueModel.ts";
|
|
|
|
const everything: OperatorQueueFacts = {
|
|
retryable: 2,
|
|
outcomeUnknown: 1,
|
|
queueableUnattempted: 3,
|
|
pausable: 4,
|
|
paused: 5,
|
|
cancellable: 14
|
|
};
|
|
|
|
const allPermissions: OperatorQueuePermissions = {
|
|
canRead: true,
|
|
canOpenReport: true,
|
|
canControl: true,
|
|
canRetry: true,
|
|
canQueue: true,
|
|
canReconcile: true
|
|
};
|
|
|
|
test("enables every fixed queue action when state and permission allow it", () => {
|
|
assert.deepEqual(operatorQueueActionBlocks(everything, allPermissions), {
|
|
details: null,
|
|
report: null,
|
|
pause: null,
|
|
resume: null,
|
|
retry: null,
|
|
"queue-unsent": null,
|
|
reconcile: null,
|
|
cancel: null
|
|
});
|
|
});
|
|
|
|
test("retains every unavailable action with a state-specific explanation", () => {
|
|
assert.deepEqual(
|
|
operatorQueueActionBlocks(
|
|
{ retryable: 0, outcomeUnknown: 0, queueableUnattempted: 0, pausable: 0, paused: 0, cancellable: 0 },
|
|
allPermissions
|
|
),
|
|
{
|
|
details: null,
|
|
report: null,
|
|
pause: OPERATOR_QUEUE_REASON.noQueued,
|
|
resume: OPERATOR_QUEUE_REASON.noPaused,
|
|
retry: OPERATOR_QUEUE_REASON.noFailed,
|
|
"queue-unsent": OPERATOR_QUEUE_REASON.noUnsent,
|
|
reconcile: OPERATOR_QUEUE_REASON.noUnknown,
|
|
cancel: OPERATOR_QUEUE_REASON.noCancellable
|
|
}
|
|
);
|
|
});
|
|
|
|
test("permission explanations take precedence over queue state", () => {
|
|
const noPermissions: OperatorQueuePermissions = {
|
|
canRead: false,
|
|
canOpenReport: false,
|
|
canControl: false,
|
|
canRetry: false,
|
|
canQueue: false,
|
|
canReconcile: false
|
|
};
|
|
const blocks = operatorQueueActionBlocks(everything, noPermissions);
|
|
assert.deepEqual(
|
|
Object.values(blocks),
|
|
Array.from({ length: 8 }, () => OPERATOR_QUEUE_REASON.permissionDenied)
|
|
);
|
|
});
|
|
|
|
test("allows in-page job selection without report permission while keeping report access disabled", () => {
|
|
const blocks = operatorQueueActionBlocks(everything, {
|
|
...allPermissions,
|
|
canOpenReport: false
|
|
});
|
|
assert.equal(blocks.details, null);
|
|
assert.equal(blocks.report, OPERATOR_QUEUE_REASON.permissionDenied);
|
|
});
|
|
|
|
test("keeps historical active work visible after a new draft becomes current", () => {
|
|
const items = campaignVersionWorkItems(
|
|
"campaign-1",
|
|
"version-2",
|
|
[
|
|
{ id: "version-2", version_number: 2 },
|
|
{ id: "version-1", version_number: 1 }
|
|
],
|
|
{
|
|
"version-2": { state: "draft", queued: 0, failed: 0 },
|
|
"version-1": { state: "active", queued: 3, failed: 1 }
|
|
}
|
|
);
|
|
|
|
assert.deepEqual(items.map((item) => [item.versionId, item.isCurrent]), [
|
|
["version-2", true],
|
|
["version-1", false]
|
|
]);
|
|
assert.deepEqual(items[1].summary, { state: "active", queued: 3, failed: 1 });
|
|
});
|
|
|
|
test("campaign-wide lifecycle totals are not multiplied across version rows", () => {
|
|
assert.deepEqual(campaignLifecycleTotals([
|
|
{ pausable: 3, paused: 1, cancellable: 4 },
|
|
{ pausable: 0, paused: 2, cancellable: 2 }
|
|
]), { pausable: 3, paused: 3, cancellable: 6 });
|
|
});
|
|
|
|
test("paused jobs do not overlap with queued or active delivery counts", () => {
|
|
assert.deepEqual(operatorDeliveryStateCounts(
|
|
{ queued: 0 },
|
|
{ queued: 4, claimed: 0, sending: 0, smtpAccepted: 2, sent: 1 }
|
|
), {
|
|
queued: 0,
|
|
claimed: 0,
|
|
sending: 0,
|
|
completed: 3,
|
|
queuedOrActive: 0
|
|
});
|
|
});
|
|
|
|
test("fast polling refreshes only selected, changed, uncached, active, or attention versions", () => {
|
|
const baseline = {
|
|
fullDiscovery: false,
|
|
versionChanged: false,
|
|
selected: false,
|
|
cached: true,
|
|
queuedOrActive: 0,
|
|
needsAttention: 0
|
|
};
|
|
assert.equal(shouldRefreshOperatorVersionSummary(baseline), false);
|
|
for (const override of [
|
|
{ fullDiscovery: true },
|
|
{ versionChanged: true },
|
|
{ selected: true },
|
|
{ cached: false },
|
|
{ queuedOrActive: 1 },
|
|
{ needsAttention: 1 }
|
|
]) {
|
|
assert.equal(shouldRefreshOperatorVersionSummary({ ...baseline, ...override }), true);
|
|
}
|
|
});
|