Expose control_print, and say what the gateway's ok does not mean

The route's body is `PrinterCommandSchema` straight from the shared schema,
so adding the variant there is what exposes it here - that is the point of
taking the type from one place. What needed saying is in the comments: stop
is a cancel with no undo, and a gateway returning ok means the printer
accepted the command rather than that the print state changed, since an idle
P1P answers success to all three verbs. A UI that puts stop where pause is
expected, or that treats the 200 as confirmation, gets this wrong.

The contract test gains the three control frames and one guard: a verb only
one vendor recognises - `cancel`, Moonraker's word for it - must not
validate, or it reaches a gateway that cannot map it. Mirrored in
continuum-proxy/tests/uplink_protocol.rs.
This commit is contained in:
2026-10-11 17:38:42 +00:00
parent b42cbac299
commit 398a739edd
2 changed files with 52 additions and 5 deletions
+40 -3
View File
@@ -87,6 +87,24 @@ describe("backend -> gateway frames", () => {
printer_id: "p1p",
command: { action: "send_gcode_line", line: "M106 S0" },
},
{
type: "command",
command_id: "9a7d2f12-0000-4000-8000-000000000000",
printer_id: "p1p",
command: { action: "control_print", control: "pause" },
},
{
type: "command",
command_id: "9a7d2f13-0000-4000-8000-000000000000",
printer_id: "p1p",
command: { action: "control_print", control: "resume" },
},
{
type: "command",
command_id: "9a7d2f14-0000-4000-8000-000000000000",
printer_id: "p1p",
command: { action: "control_print", control: "stop" },
},
];
for (const frame of outbound) {
@@ -95,10 +113,29 @@ describe("backend -> gateway frames", () => {
});
}
it("rejects a print control verb the gateway does not implement", () => {
// "cancel" is the trap: it is what Moonraker calls this, and the Klipper
// implementation behind `control_print` really does send a cancel - but
// the wire word is "stop". A verb that only one vendor recognises must
// not validate here, or it reaches a gateway that cannot map it.
for (const control of ["cancel", "abort", "Pause", ""]) {
expect(
Value.Check(EdgeServerMessageSchema, {
type: "command",
command_id: "x",
printer_id: "p1p",
command: { action: "control_print", control },
}),
).toBe(false);
}
});
it("has no start-print command to send", () => {
// Deliberate: the gateway can start an uploaded file, but that path has
// never been run against real hardware. Delete this test when it has been,
// and add the variant to the schema.
// Still deliberate, but no longer for the original reason: starting an
// uploaded file *has* now been confirmed on a real P1P (2026-10-11).
// What is missing is the path around it - nothing uploads a sliced file
// to a gateway and nothing decides which printer runs which job. Delete
// this test when there is something to start.
expect(
Value.Check(EdgeServerMessageSchema, {
type: "command",