Files
octoturge 847f7cb686 Raise alerts: failed or paused prints, and AI detections
Nothing subscribed to channel:print_alerts and nothing watched a printer
fail, so an operator away from the screen found out when they came back to
a ruined plate. Alerts are now rows in printer_alerts, listed at
GET /alerts (open=true for what still needs a person) and cleared with
POST /alerts/:id/acknowledge, which is operator-only and keeps the first
acknowledger on the record.

Two sources. Telemetry: a change into error is a failed print if the printer
was printing or paused, otherwise a printer error; a print the printer
paused itself (how an AMS fault looks) is announced too. Transitions, not
levels - a printer sitting in error is one alert, not one per frame - and a
connection's first sight of a printer raises nothing, so a gateway that
reconnects does not re-announce known faults. AI: the worker's Valkey
publications are stored when confidence is at least ALERT_MIN_CONFIDENCE
(default 0.6); the worker deliberately does not threshold, so the policy
lives here. A bad message costs one message, not the listener.

e2e-authz.sh gains the scoping/ack cases; new e2e-alerts.sh drives a stand-in
gateway over the real /ws/edge/v1 and the real Valkey channel (checked to
fail when the derivation is broken).
2026-10-29 14:56:24 +00:00

76 lines
3.1 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import { deriveAlert, minAiConfidence, worthRaising } from "../src/alerts/derive";
describe("deriveAlert", () => {
test("a fault during a print is a failed print", () => {
expect(deriveAlert("printing", "error", "H2C")?.kind).toBe("print_failed");
expect(deriveAlert("paused", "error", "H2C")?.kind).toBe("print_failed");
});
test("a fault on an idle printer is a printer error, not a failed print", () => {
expect(deriveAlert("idle", "error", "P1P")?.kind).toBe("printer_error");
expect(deriveAlert("offline", "error", "P1P")?.kind).toBe("printer_error");
});
test("the printer pausing itself is announced, with its own words", () => {
const alert = deriveAlert("printing", "paused", "H2C", "PAUSE");
expect(alert?.kind).toBe("print_paused");
expect(alert?.message).toBe("H2C: the print is paused (printer reports PAUSE)");
});
test("a level is not an alert: staying in error raises nothing", () => {
expect(deriveAlert("error", "error", "H2C")).toBeUndefined();
});
test("a printer first seen on this connection raises nothing", () => {
expect(deriveAlert(undefined, "error", "H2C")).toBeUndefined();
});
test("an unmapped status never raises", () => {
expect(deriveAlert("printing", undefined, "H2C")).toBeUndefined();
});
test("ordinary transitions are quiet", () => {
expect(deriveAlert("idle", "printing", "H2C")).toBeUndefined();
expect(deriveAlert("printing", "idle", "H2C")).toBeUndefined();
expect(deriveAlert("paused", "printing", "H2C")).toBeUndefined();
});
});
describe("AI detection floor", () => {
test("defaults to 0.6 and respects the environment", () => {
expect(minAiConfidence({})).toBe(0.6);
expect(minAiConfidence({ ALERT_MIN_CONFIDENCE: "0.9" })).toBe(0.9);
});
test("an out-of-range or non-numeric floor falls back rather than disabling alerts", () => {
expect(minAiConfidence({ ALERT_MIN_CONFIDENCE: "7" })).toBe(0.6);
expect(minAiConfidence({ ALERT_MIN_CONFIDENCE: "lots" })).toBe(0.6);
});
test("worthRaising is inclusive at the floor", () => {
expect(worthRaising(0.6, 0.6)).toBe(true);
expect(worthRaising(0.59, 0.6)).toBe(false);
});
});
import { parsePrintAlert } from "../src/alerts/parse";
describe("parsePrintAlert", () => {
const good = {
printer_id: "0b6d3b4e-8f58-4a3b-9c69-9f3a2f6a0a11",
label: "spaghetti",
confidence: 0.91,
captured_at: "2026-10-29T10:00:00Z",
r2_object_key: "frames/x.jpg",
};
test("accepts exactly the published shape", () => {
expect(parsePrintAlert(JSON.stringify(good))?.label).toBe("spaghetti");
});
test("rejects garbage, missing fields and out-of-range confidence instead of throwing", () => {
expect(parsePrintAlert("not json")).toBeUndefined();
expect(parsePrintAlert(JSON.stringify({ ...good, printer_id: undefined }))).toBeUndefined();
expect(parsePrintAlert(JSON.stringify({ ...good, confidence: 3 }))).toBeUndefined();
// Tolerant of extra fields: a newer worker adding one must not blind this consumer.
expect(parsePrintAlert(JSON.stringify({ ...good, extra: 1 }))?.label).toBe("spaghetti");
});
});