Files
openclaw/src/worker/node-supervisor-protocol.test.ts
T
Peter Steinberger afb3b9defb fix(worker): surface gateway admission failures (#123773)
* fix(worker): surface admission retry failures

* fix(worker): ignore cancellation connection errors
2026-08-14 13:29:36 -07:00

100 lines
3.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
NODE_WORKER_CONNECTION_FAILURE_MESSAGE_TYPE,
parseNodeWorkerConnectionFailureMessage,
parseNodeWorkerSupervisorReceipt,
type NodeWorkerSupervisorIdentity,
} from "./node-supervisor-protocol.js";
const RESULT_JSON_MAX_BYTES = 64 * 1024;
const ERROR_TEXT_MAX_BYTES = 4 * 1024;
const identity: NodeWorkerSupervisorIdentity = {
launchId: "launch-1",
planHash: "a".repeat(64),
environmentId: "environment-1",
sessionId: "session-1",
ownerEpoch: 3,
placementGeneration: 4,
runId: "run-1",
};
describe("node worker supervisor wire receipt", () => {
it("accepts only bounded worker connection diagnostics", () => {
expect(
parseNodeWorkerConnectionFailureMessage({
type: NODE_WORKER_CONNECTION_FAILURE_MESSAGE_TYPE,
cause: "certificate rejected",
}),
).toEqual({
type: NODE_WORKER_CONNECTION_FAILURE_MESSAGE_TYPE,
cause: "certificate rejected",
});
expect(
parseNodeWorkerConnectionFailureMessage({
type: NODE_WORKER_CONNECTION_FAILURE_MESSAGE_TYPE,
cause: null,
}),
).toEqual({ type: NODE_WORKER_CONNECTION_FAILURE_MESSAGE_TYPE, cause: null });
expect(
parseNodeWorkerConnectionFailureMessage({
type: NODE_WORKER_CONNECTION_FAILURE_MESSAGE_TYPE,
cause: "x".repeat(64 * 1024 + 1),
}),
).toBeNull();
});
it.each([
{ ...identity, state: "pending" },
{ ...identity, state: "running" },
{
...identity,
state: "completed",
resultJson: JSON.stringify({ status: "completed", transcriptNextSeq: 2 }),
},
{ ...identity, state: "failed", errorText: "worker exited before completion" },
{ ...identity, state: "interrupted", errorText: "node host stopped" },
{ ...identity, state: "cancelled", errorText: "node worker launch cancelled" },
])("round-trips the closed $state receipt", (receipt) => {
expect(parseNodeWorkerSupervisorReceipt(receipt)).toEqual(receipt);
});
it.each([
{ name: "extra field", receipt: { ...identity, state: "running", workerPid: 123 } },
{ name: "missing plan hash", receipt: { ...identity, planHash: undefined, state: "running" } },
{ name: "completed without output", receipt: { ...identity, state: "completed" } },
{
name: "completed with malformed output",
receipt: { ...identity, state: "completed", resultJson: "{" },
},
{
name: "oversized completed output",
receipt: {
...identity,
state: "completed",
resultJson: JSON.stringify({ text: "x".repeat(RESULT_JSON_MAX_BYTES) }),
},
},
{ name: "failed without error", receipt: { ...identity, state: "failed" } },
{
name: "multiline error",
receipt: { ...identity, state: "failed", errorText: "first\nsecond" },
},
{
name: "oversized error",
receipt: {
...identity,
state: "failed",
errorText: "x".repeat(ERROR_TEXT_MAX_BYTES + 1),
},
},
])("rejects $name", ({ receipt }) => {
expect(parseNodeWorkerSupervisorReceipt(receipt)).toBeNull();
});
it("rejects non-object values without throwing", () => {
expect(parseNodeWorkerSupervisorReceipt("{")).toBeNull();
expect(parseNodeWorkerSupervisorReceipt(null)).toBeNull();
});
});