import { describe, expect, test } from "bun:test"; import { nextJobStatus } from "../src/dispatch/job_status"; /** * The job-status mapping, tested exhaustively. * * This is the logic that decides whether a print happened. It exists because * a gateway accepting `start_print` says nothing about the outcome: a real * end-to-end run on a P1P (2026-10-15) was accepted, uploaded byte-perfect, * reached RUNNING and then the printer paused itself on a physical * broken-filament fault. Job state is therefore derived from the telemetry * that follows, and getting this table wrong means the API reports prints * that never ran - the one failure nobody would catch by looking. */ describe("nextJobStatus", () => { const currents = ["uploading", "printing", "paused"] as const; const printerStates = ["idle", "printing", "paused", "error"] as const; // Written out rather than computed: a table derived the same way the // implementation derives it agrees with a wrong implementation. const expected: Record = { "uploading/idle": undefined, "uploading/printing": "printing", "uploading/paused": "paused", "uploading/error": "failed", "printing/idle": "completed", "printing/printing": "printing", "printing/paused": "paused", "printing/error": "failed", "paused/idle": "completed", "paused/printing": "printing", "paused/paused": "paused", "paused/error": "failed", }; for (const current of currents) { for (const printerState of printerStates) { const key = `${current}/${printerState}`; test(`${key} -> ${expected[key] ?? "unchanged"}`, () => { expect(nextJobStatus(current, printerState)).toBe(expected[key] as never); }); } } test("an idle printer does NOT complete a job that was still uploading", () => { // The case worth its own test. A printer is idle for the entire transfer // - minutes of it, at the 0.2 MiB/s measured over FTPS - so "idle" there // means "has not started", not "finished". Reading it as completion // would mark every job successful seconds after dispatch, before the // file had even reached the machine. expect(nextJobStatus("uploading", "idle")).toBeUndefined(); }); test("a printer error fails the job from any state, including mid-upload", () => { // The P1P's self-pause arrived while the job was still `uploading` by // the cloud's reckoning, because the printer faulted during its // pre-print stages. If `uploading` were excluded here, that job would // have sat claiming to upload until the reaper got it twenty minutes // later. for (const current of currents) { expect(nextJobStatus(current, "error")).toBe("failed"); } }); test("completion is never reported from a state the print had not begun in", () => { // Guards the invariant rather than the table: the only route to // "completed" is through a state that means the printer had actually // been printing. const completable = currents.filter((c) => nextJobStatus(c, "idle") === "completed"); expect(completable).toEqual(["printing", "paused"]); }); }); describe("the dispatch switch", () => { test("is on by default and off only for the exact string false", async () => { const { dispatchEnabled } = await import("../src/dispatch/enabled"); expect(dispatchEnabled({})).toBe(true); expect(dispatchEnabled({ DISPATCH_ENABLED: "true" })).toBe(true); expect(dispatchEnabled({ DISPATCH_ENABLED: "false" })).toBe(false); }); test("a value that looks like an attempt to disable it but does not is reported", async () => { const { dispatchEnabled, dispatchSwitchWarning } = await import("../src/dispatch/enabled"); for (const typo of ["0", "off", "False", "no"]) { expect(dispatchEnabled({ DISPATCH_ENABLED: typo })).toBe(true); expect(dispatchSwitchWarning({ DISPATCH_ENABLED: typo })).toContain("WILL be dispatched"); } expect(dispatchSwitchWarning({})).toBeUndefined(); expect(dispatchSwitchWarning({ DISPATCH_ENABLED: "false" })).toBeUndefined(); }); });