Files
octoturge cd34f7b597 Prove the whole print loop with every machine but the printer real
e2e-job-flow.sh uploads a file through the presigned URL to the real object
store, lets the real scheduler dispatch it over /ws/edge/v1 to a stand-in
gateway, which downloads it through the presigned GET and hashes it, then
reports the print running and finished. The job must pass through printing
to completed and the gateway must have received exactly the uploaded bytes.

Dispatch is on in this one, which is only safe because the stand-in is the
only gateway the backend instance under test can have; the script says so.
2026-10-29 15:16:39 +00:00

85 lines
4.2 KiB
TypeScript

/**
* A stand-in gateway that stays connected and answers printer provisioning,
* for scripts/e2e-provision.sh.
*
* With TELEMETRY_AFTER_PROVISION=1 it also reports each provisioned printer
* (running, then failed a few seconds later), so the console has something to
* alert on.
*
* With REPORT_IDLE=<gateway-printer-id> it reports that printer idle on
* connect, so the scheduler treats it as free. A `start_print` command is
* accepted, the file is downloaded from its presigned URL and hashed (the
* hash is appended to the frames file, never the URL), and the printer then
* reports running and finished, like a real print that goes well.
*
* Needs FARM_ID in the environment (the hello must name the token's farm).
*
* Usage: bun scripts/fake-gateway-serve.ts <ws-url> <edge-jwt> <seconds> <frames-file> [refuse-access-code]
*
* Every provision_printer / remove_printer frame it receives is appended to
* <frames-file> as one JSON line, so the script can prove the credential
* arrived here. A provision whose access_code equals <refuse-access-code> is
* answered as a failure, like a printer that rejected the login.
*/
import { appendFileSync } from "node:fs";
const [url, jwt, seconds, framesFile, refuseCode] = process.argv.slice(2);
if (!url || !jwt || !seconds || !framesFile) {
console.error("usage: fake-gateway-serve.ts <ws-url> <edge-jwt> <seconds> <frames-file> [refuse-code]");
process.exit(2);
}
const ws = new WebSocket(url, { headers: { authorization: `Bearer ${jwt}` } } as never);
ws.onerror = () => {
console.error("socket error");
process.exit(1);
};
ws.onopen = () => {
// farm_id is required by the hello schema; the backend checks it against the token's farm.
ws.send(JSON.stringify({ type: "hello", gateway_id: "fake-gw", farm_id: process.env.FARM_ID }));
if (process.env.REPORT_IDLE) {
ws.send(JSON.stringify({ type: "telemetry", printers: [{ printer_id: process.env.REPORT_IDLE, name: "Fake", state: "IDLE", percent: 0, print_error: 0 }] }));
}
console.log("ready");
};
async function printFile(message: { command_id: string; printer_id: string; command: { job_id: string; file_name: string; url: string } }) {
const report = (state: string, percent: number) =>
ws.send(JSON.stringify({ type: "telemetry", printers: [{ printer_id: message.printer_id, name: "Fake", state, percent, print_error: 0 }] }));
const body = new Uint8Array(await (await fetch(message.command.url)).arrayBuffer());
const digest = new Bun.CryptoHasher("sha256").update(body).digest("hex");
appendFileSync(framesFile, JSON.stringify({ type: "downloaded", file_name: message.command.file_name, bytes: body.length, sha256: digest }) + "\n");
report("RUNNING", 20);
setTimeout(() => report("RUNNING", 60), 1500);
setTimeout(() => report("FINISH", 100), 3000);
}
ws.onmessage = (event) => {
const message = JSON.parse(String(event.data));
if (message.type === "command" && message.command?.action === "start_print") {
appendFileSync(framesFile, JSON.stringify({ type: "start_print", printer_id: message.printer_id, file_name: message.command.file_name, job_id: message.command.job_id }) + "\n");
ws.send(JSON.stringify({ type: "command_result", command_id: message.command_id, ok: true }));
void printFile(message);
return;
}
if (message.type !== "provision_printer" && message.type !== "remove_printer") return;
appendFileSync(framesFile, JSON.stringify(message) + "\n");
if (message.type === "provision_printer" && process.env.TELEMETRY_AFTER_PROVISION === "1") {
const id = message.printer.id;
const report = (state: string) =>
ws.send(JSON.stringify({ type: "telemetry", printers: [{ printer_id: id, name: message.printer.name, state, percent: 40, print_error: 0 }] }));
setTimeout(() => report("RUNNING"), 1500);
setTimeout(() => report("RUNNING"), 3000);
setTimeout(() => report("FAILED"), 4500);
}
const refused = message.type === "provision_printer" && refuseCode && message.printer.access_code === refuseCode;
ws.send(
JSON.stringify({
type: "command_result",
command_id: message.request_id,
ok: !refused,
...(refused ? { error: "could not connect to the printer: the access code was rejected" } : {}),
}),
);
};
setTimeout(() => process.exit(0), Number(seconds) * 1000);