Files
paseo/packages/workspace-runtime-contract/test/contract.test.mjs
T
Mohamed Boudra 359d5443a9 feat(workspaces): probe providers in selected runtimes
Pre-creation provider truth now comes from an invisible real runtime workspace and reuses the normal provider snapshot pipeline. Probe records are owned separately, keeping them structurally absent from ordinary workspace projections.

Setup eligibility follows fresh content materialization rather than lifecycle resource ownership. Adopted Local directories never execute repository setup, while fresh Worktree, Docker, and external content may; provider probes never execute repository setup.
2026-08-12 13:02:21 +02:00

584 lines
21 KiB
JavaScript

import { execFile, spawn } from "node:child_process";
import { mkdir, mkdtemp, readFile, rm, stat } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import test from "node:test";
import assert from "node:assert/strict";
import { promisify } from "node:util";
import {
CommandRuntimeControlSchema,
CommandRuntimeDescribeResponseSchema,
CommandRuntimeLifecycleRequestSchema,
CommandRuntimeLifecycleResponseSchema,
CommandRuntimeProcessEventSchema,
CommandRuntimeProjectSourceSchema,
CommandRuntimePlacementIntentSchema,
CommandRuntimeCreateInputSchema,
CommandRuntimeInspectionSchema,
CommandRuntimePlacementSchema,
CommandRuntimeProcessPurposeSchema,
CommandRuntimeSpawnEnvelopeSchema,
CommandRuntimeStdioSchema,
CommandRuntimeStateSchema,
createCommandRuntimeMessageDecoder,
createCommandRuntimeProcessEventDecoder,
encodeCommandRuntimeMessage,
} from "../dist/index.js";
const run = promisify(execFile);
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const examples = JSON.parse(await readFile(path.join(packageRoot, "examples/v1.json"), "utf8"));
test("documented lifecycle, fd3, control, and fd4 examples are exact newline-terminated bytes", () => {
for (const [name, schema] of [
["describeResponse", CommandRuntimeDescribeResponseSchema],
["lifecycleRequest", CommandRuntimeLifecycleRequestSchema],
["lifecycleResponse", CommandRuntimeLifecycleResponseSchema],
["pipeSpawnControl", CommandRuntimeControlSchema],
["ptySpawnControl", CommandRuntimeControlSchema],
["resizeControl", CommandRuntimeControlSchema],
["signalControl", CommandRuntimeControlSchema],
["startedEvent", CommandRuntimeProcessEventSchema],
["eofEvent", CommandRuntimeProcessEventSchema],
["resizedEvent", CommandRuntimeProcessEventSchema],
["exitEvent", CommandRuntimeProcessEventSchema],
["errorEvent", CommandRuntimeProcessEventSchema],
]) {
const bytes = encodeCommandRuntimeMessage(schema, examples[name]);
assert.equal(bytes, examples.bytes[name]);
assert.equal(bytes.endsWith("\n"), true);
assert.equal(bytes.endsWith("\n\n"), false);
}
});
test("lifecycle uses one exact stdin request and one exact stdout response", async () => {
assert.equal(examples.lifecycleRequest.input.purpose, "provider-probe");
assert.deepEqual(examples.lifecycleResponse.state, {
workspaceId: "workspace-01",
lifecycle: "ready",
});
assert.equal(examples.lifecycleResponse.materializedFreshContent, true);
assert.doesNotMatch(examples.bytes.lifecycleResponse, /root|revision|executionDomainId/);
const child = spawn(
process.execPath,
[path.join(packageRoot, "test/pipe-fixture.mjs"), "lifecycle"],
{
stdio: ["pipe", "pipe", "pipe"],
},
);
child.stdin.end(examples.bytes.lifecycleRequest);
const [stdout, stderr, exit] = await Promise.all([
collect(child.stdout),
collect(child.stderr),
exited(child),
]);
assert.deepEqual(exit, { code: 0, signal: null });
assert.equal(stdout.toString(), examples.bytes.lifecycleResponse);
assert.equal(stderr.length, 0);
});
test("pipes use one fd3 spawn then started/eof/exit on fd4 with raw fd0/1/2", async () => {
const child = spawn(process.execPath, [path.join(packageRoot, "test/pipe-fixture.mjs")], {
stdio: ["pipe", "pipe", "pipe", "pipe", "pipe"],
});
child.stdin.end(Buffer.from("raw\u0000stdin\n"));
child.stdio[3].end(examples.bytes.pipeSpawnControl);
const [stdout, stderr, events, exit] = await Promise.all([
collect(child.stdout),
collect(child.stderr),
collect(child.stdio[4]),
exited(child),
]);
assert.deepEqual(exit, { code: 0, signal: null });
assert.deepEqual(stdout, Buffer.from([0x00, 0x6f, 0x75, 0x74, 0xff]));
assert.deepEqual(stderr, Buffer.from([0x65, 0x72, 0x72, 0x00]));
assert.equal(
events.toString(),
examples.bytes.startedEvent + examples.bytes.eofEvent + examples.bytes.exitEvent,
);
assert.deepEqual(decodeEvents(events), [
examples.startedEvent,
examples.eofEvent,
examples.exitEvent,
]);
});
test("PTY keeps fd3 open for resize and signal through EOF and emits a valid fd4 sequence", async () => {
const child = spawn(process.execPath, [path.join(packageRoot, "test/pty-fixture.mjs")], {
stdio: ["pipe", "pipe", "pipe", "pipe", "pipe"],
});
child.stdin.end(Buffer.from([0x74, 0x65, 0x72, 0x6d, 0x00, 0xff]));
child.stdio[3].write(examples.bytes.ptySpawnControl);
child.stdio[3].write(examples.bytes.resizeControl);
child.stdio[3].end(examples.bytes.signalControl);
const [stdout, stderr, events, exit] = await Promise.all([
collect(child.stdout),
collect(child.stderr),
collect(child.stdio[4]),
exited(child),
]);
assert.deepEqual(exit, { code: 0, signal: null });
assert.deepEqual(stdout, Buffer.from([0x74, 0x65, 0x72, 0x6d, 0x00, 0xff]));
assert.deepEqual(stderr, Buffer.from([0x70, 0x74, 0x79, 0x00]));
assert.equal(
events.toString(),
examples.bytes.startedEvent +
examples.bytes.resizedEvent +
examples.bytes.eofEvent +
'{"type":"exit","protocolVersion":1,"code":null,"signal":"SIGTERM"}\n',
);
assert.deepEqual(
decodeEvents(events).map((event) => event.type),
["started", "resized", "eof", "exit"],
);
});
test("actual fd4 rejects malformed and wrong-version frames and detects early wrapper exit", async () => {
for (const [mode, pattern] of [
["malformed", SyntaxError],
["wrong-version", /expected 1/],
]) {
const child = spawn(process.execPath, [path.join(packageRoot, "test/pipe-fixture.mjs"), mode], {
stdio: ["ignore", "ignore", "ignore", "ignore", "pipe"],
});
const [events, exit] = await Promise.all([collect(child.stdio[4]), exited(child)]);
assert.deepEqual(exit, { code: 0, signal: null });
assert.throws(() => decodeEvents(events), pattern);
}
const early = spawn(
process.execPath,
[path.join(packageRoot, "test/pipe-fixture.mjs"), "early-exit"],
{ stdio: ["ignore", "ignore", "ignore", "ignore", "pipe"] },
);
const [events, exit] = await Promise.all([collect(early.stdio[4]), exited(early)]);
assert.equal(events.length, 0);
assert.deepEqual(exit, { code: 47, signal: null });
});
test("framing accepts partial bytes and rejects malformed, early EOF, and wrong versions", () => {
const partial = createCommandRuntimeMessageDecoder(CommandRuntimeProcessEventSchema);
assert.deepEqual(partial.push(examples.bytes.exitEvent.slice(0, 11)), []);
assert.deepEqual(partial.push(examples.bytes.exitEvent.slice(11)), [examples.exitEvent]);
partial.finish();
const malformed = createCommandRuntimeMessageDecoder(CommandRuntimeProcessEventSchema);
assert.throws(() => malformed.push("{nope}\n"), SyntaxError);
const early = createCommandRuntimeMessageDecoder(CommandRuntimeProcessEventSchema);
early.push(examples.bytes.exitEvent.slice(0, -1));
assert.throws(() => early.finish(), /ended before newline/);
const wrongVersion = createCommandRuntimeMessageDecoder(CommandRuntimeProcessEventSchema);
assert.throws(
() => wrongVersion.push('{"type":"exit","protocolVersion":2,"code":0,"signal":null}\n'),
/expected 1/,
);
});
test("process event conversations enforce the complete mode-specific lifecycle", () => {
for (const [mode, events] of [
["pipes", [examples.startedEvent, examples.eofEvent, examples.exitEvent]],
["pty", [examples.startedEvent, examples.resizedEvent, examples.eofEvent, examples.exitEvent]],
]) {
const decoder = createCommandRuntimeProcessEventDecoder(mode);
assert.deepEqual(
decoder.push(
events
.map((event) => encodeCommandRuntimeMessage(CommandRuntimeProcessEventSchema, event))
.join(""),
),
events,
);
decoder.finish();
}
for (const [name, mode, events, pattern] of [
["exit before eof", "pipes", [examples.startedEvent, examples.exitEvent], /exit before eof/],
["eof before started", "pipes", [examples.eofEvent], /eof before started/],
["exit before started", "pipes", [examples.exitEvent], /exit before started/],
[
"duplicate started",
"pipes",
[examples.startedEvent, examples.startedEvent],
/duplicate started/,
],
[
"duplicate eof",
"pipes",
[examples.startedEvent, examples.eofEvent, examples.eofEvent],
/duplicate eof/,
],
[
"duplicate exit",
"pipes",
[examples.startedEvent, examples.eofEvent, examples.exitEvent, examples.exitEvent],
/duplicate exit/,
],
[
"post-exit lifecycle",
"pipes",
[examples.startedEvent, examples.eofEvent, examples.exitEvent, examples.eofEvent],
/event after exit/,
],
[
"wrong-mode resize",
"pipes",
[examples.startedEvent, examples.resizedEvent],
/pipes.*resized/,
],
["PTY resize before started", "pty", [examples.resizedEvent], /resized before started/],
[
"PTY resize after eof",
"pty",
[examples.startedEvent, examples.eofEvent, examples.resizedEvent],
/resized after eof/,
],
[
"PTY post-exit acknowledgement",
"pty",
[examples.startedEvent, examples.eofEvent, examples.exitEvent, examples.resizedEvent],
/event after exit/,
],
]) {
const decoder = createCommandRuntimeProcessEventDecoder(mode);
assert.throws(
() =>
decoder.push(
events
.map((event) => encodeCommandRuntimeMessage(CommandRuntimeProcessEventSchema, event))
.join(""),
),
pattern,
name,
);
}
});
test("every public v1 object rejects unknown authority at every nesting level", () => {
const createInput = examples.lifecycleRequest.input;
const gitSource = {
kind: "git",
url: "https://example.invalid/repository.git",
revision: "main",
subdirectory: "project",
};
const resolvedPlacement = {
kind: "resolved-worktree",
worktreeSlug: "change-7",
source: {
kind: "checkout-change-request",
headRef: "feature",
baseRefName: "main",
checkoutRefs: [{ remoteName: "origin", remoteRef: "refs/heads/feature" }],
},
};
const readyInspection = {
status: "ready",
state: examples.lifecycleResponse.state,
placement: examples.lifecycleResponse.placement,
};
const pausedInspection = {
status: "paused",
state: { ...examples.lifecycleResponse.state, lifecycle: "paused" },
placement: examples.lifecycleResponse.placement,
};
const strictCases = [
["describe", CommandRuntimeDescribeResponseSchema, examples.describeResponse, []],
["create request", CommandRuntimeLifecycleRequestSchema, examples.lifecycleRequest, []],
[
"create request input",
CommandRuntimeLifecycleRequestSchema,
examples.lifecycleRequest,
["input"],
],
[
"inspect request",
CommandRuntimeLifecycleRequestSchema,
{ protocolVersion: 1, options: {} },
[],
],
[
"pause request",
CommandRuntimeLifecycleRequestSchema,
{ protocolVersion: 1, options: {} },
[],
],
[
"resume request",
CommandRuntimeLifecycleRequestSchema,
{ protocolVersion: 1, options: {} },
[],
],
[
"destroy request",
CommandRuntimeLifecycleRequestSchema,
{ protocolVersion: 1, options: {} },
[],
],
["create input", CommandRuntimeCreateInputSchema, createInput, []],
["create project", CommandRuntimeCreateInputSchema, createInput, ["project"]],
["create source", CommandRuntimeCreateInputSchema, createInput, ["project", "source"]],
["create placement", CommandRuntimeCreateInputSchema, createInput, ["placement"]],
["host source", CommandRuntimeProjectSourceSchema, createInput.project.source, []],
["git source", CommandRuntimeProjectSourceSchema, gitSource, []],
["existing placement", CommandRuntimePlacementIntentSchema, createInput.placement, []],
[
"branch placement",
CommandRuntimePlacementIntentSchema,
{ kind: "branch", branchName: "feature", baseRef: "main" },
[],
],
[
"checkout placement",
CommandRuntimePlacementIntentSchema,
{ kind: "checkout", ref: "feature" },
[],
],
["resolved placement", CommandRuntimePlacementIntentSchema, resolvedPlacement, []],
["resolved source", CommandRuntimePlacementIntentSchema, resolvedPlacement, ["source"]],
[
"resolved checkout ref",
CommandRuntimePlacementIntentSchema,
resolvedPlacement,
["source", "checkoutRefs", 0],
],
["state", CommandRuntimeStateSchema, examples.lifecycleResponse.state, []],
["placement", CommandRuntimePlacementSchema, examples.lifecycleResponse.placement, []],
[
"create state response",
CommandRuntimeLifecycleResponseSchema,
examples.lifecycleResponse,
[],
],
[
"resume state response",
CommandRuntimeLifecycleResponseSchema,
examples.lifecycleResponse,
[],
],
[
"state response state",
CommandRuntimeLifecycleResponseSchema,
examples.lifecycleResponse,
["state"],
],
[
"state response placement",
CommandRuntimeLifecycleResponseSchema,
examples.lifecycleResponse,
["placement"],
],
["ready inspection", CommandRuntimeInspectionSchema, readyInspection, []],
["ready inspection state", CommandRuntimeInspectionSchema, readyInspection, ["state"]],
["ready inspection placement", CommandRuntimeInspectionSchema, readyInspection, ["placement"]],
["paused inspection", CommandRuntimeInspectionSchema, pausedInspection, []],
["paused inspection state", CommandRuntimeInspectionSchema, pausedInspection, ["state"]],
[
"paused inspection placement",
CommandRuntimeInspectionSchema,
pausedInspection,
["placement"],
],
["missing inspection", CommandRuntimeInspectionSchema, { status: "missing" }, []],
[
"error inspection",
CommandRuntimeInspectionSchema,
{ status: "error", message: "failure" },
[],
],
[
"inspection response",
CommandRuntimeLifecycleResponseSchema,
{ protocolVersion: 1, type: "inspection", inspection: { status: "missing" } },
[],
],
[
"ready inspection response state",
CommandRuntimeLifecycleResponseSchema,
{ protocolVersion: 1, type: "inspection", inspection: readyInspection },
["inspection", "state"],
],
[
"ready inspection response placement",
CommandRuntimeLifecycleResponseSchema,
{ protocolVersion: 1, type: "inspection", inspection: readyInspection },
["inspection", "placement"],
],
[
"nested inspection",
CommandRuntimeLifecycleResponseSchema,
{ protocolVersion: 1, type: "inspection", inspection: { status: "missing" } },
["inspection"],
],
[
"pause ok response",
CommandRuntimeLifecycleResponseSchema,
{ protocolVersion: 1, type: "ok" },
[],
],
[
"destroy ok response",
CommandRuntimeLifecycleResponseSchema,
{ protocolVersion: 1, type: "ok" },
[],
],
[
"agent purpose",
CommandRuntimeProcessPurposeSchema,
{ kind: "agent", agentId: "a", provider: "p" },
[],
],
[
"terminal purpose",
CommandRuntimeProcessPurposeSchema,
{ kind: "terminal", terminalId: "t" },
[],
],
["Git purpose", CommandRuntimeProcessPurposeSchema, { kind: "git" }, []],
[
"provider purpose",
CommandRuntimeProcessPurposeSchema,
{ kind: "provider-probe", provider: "p" },
[],
],
["helper purpose", CommandRuntimeProcessPurposeSchema, { kind: "workspace-helper" }, []],
["script purpose", CommandRuntimeProcessPurposeSchema, examples.pipeSpawnControl.purpose, []],
["setup purpose", CommandRuntimeProcessPurposeSchema, { kind: "setup" }, []],
["archive purpose", CommandRuntimeProcessPurposeSchema, { kind: "archive" }, []],
["pipes stdio", CommandRuntimeStdioSchema, { kind: "pipes" }, []],
["PTY stdio", CommandRuntimeStdioSchema, examples.ptySpawnControl.stdio, []],
["spawn", CommandRuntimeSpawnEnvelopeSchema, examples.pipeSpawnControl, []],
["spawn purpose", CommandRuntimeSpawnEnvelopeSchema, examples.pipeSpawnControl, ["purpose"]],
["spawn stdio", CommandRuntimeSpawnEnvelopeSchema, examples.pipeSpawnControl, ["stdio"]],
["resize control", CommandRuntimeControlSchema, examples.resizeControl, []],
["signal control", CommandRuntimeControlSchema, examples.signalControl, []],
["started event", CommandRuntimeProcessEventSchema, examples.startedEvent, []],
["eof event", CommandRuntimeProcessEventSchema, examples.eofEvent, []],
["resized event", CommandRuntimeProcessEventSchema, examples.resizedEvent, []],
["exit event", CommandRuntimeProcessEventSchema, examples.exitEvent, []],
["error event", CommandRuntimeProcessEventSchema, examples.errorEvent, []],
];
for (const [name, schema, value, nestingPath] of strictCases) {
for (const field of ["root", "unknownV1Field"]) {
assert.throws(
() => schema.parse(inject(value, nestingPath, field, "/private")),
/unrecognized key/i,
`${name} accepted ${field}`,
);
}
}
});
test("v1 extension maps stay explicit and future versions fail by protocol version", () => {
assert.deepEqual(
CommandRuntimeLifecycleRequestSchema.parse({
protocolVersion: 1,
options: { root: "runtime-private-option", vendorExtension: { nested: true } },
}).options,
{ root: "runtime-private-option", vendorExtension: { nested: true } },
);
assert.deepEqual(
CommandRuntimeStateSchema.parse({
...examples.lifecycleResponse.state,
lifecycleEnvironment: { root: "environment-value", VENDOR_VALUE: "accepted" },
}).lifecycleEnvironment,
{ root: "environment-value", VENDOR_VALUE: "accepted" },
);
assert.deepEqual(
CommandRuntimeSpawnEnvelopeSchema.parse({
...examples.pipeSpawnControl,
env: { root: "workload-environment-value" },
options: { vendorExtension: true },
}),
{
...examples.pipeSpawnControl,
env: { root: "workload-environment-value" },
options: { vendorExtension: true },
},
);
for (const [schema, value] of [
[CommandRuntimeDescribeResponseSchema, examples.describeResponse],
[CommandRuntimeLifecycleRequestSchema, examples.lifecycleRequest],
[CommandRuntimeLifecycleResponseSchema, examples.lifecycleResponse],
[CommandRuntimeControlSchema, examples.pipeSpawnControl],
[CommandRuntimeProcessEventSchema, examples.startedEvent],
]) {
assert.throws(() => schema.parse({ ...value, protocolVersion: 2 }), /expected 1/);
}
});
test("public state schemas reject physical authority under computed and aliased spellings", () => {
const root = ["ro", "ot"].join("");
const state = { workspaceId: "workspace-01", lifecycle: "ready", [root]: "/private" };
const schemaAlias = CommandRuntimeStateSchema;
assert.throws(() => schemaAlias.parse(state), /unrecognized key/i);
assert.throws(
() =>
CommandRuntimeLifecycleResponseSchema.parse({
...examples.lifecycleResponse,
state: { ...examples.lifecycleResponse.state, [root]: "/private" },
}),
/unrecognized key/i,
);
});
test("every export condition resolves from a packed standalone install", async (t) => {
const root = await mkdtemp(path.join(tmpdir(), "workspace-runtime-contract-pack-"));
t.after(() => rm(root, { recursive: true, force: true }));
const pack = JSON.parse(
(
await run("npm", ["pack", "--json", "--ignore-scripts", "--pack-destination", root], {
cwd: packageRoot,
})
).stdout,
);
const archive = path.join(root, pack[0].filename);
const installRoot = path.join(root, "standalone");
await mkdir(installRoot);
await run("npm", ["install", "--ignore-scripts", "--no-package-lock", archive], {
cwd: installRoot,
});
const installed = path.join(installRoot, "node_modules/@getpaseo/workspace-runtime-contract");
const manifest = JSON.parse(await readFile(path.join(installed, "package.json"), "utf8"));
for (const target of Object.values(manifest.exports["."])) {
await stat(path.join(installed, target));
}
const imported = await import(pathToFileURL(path.join(installed, manifest.exports["."].default)));
assert.equal(imported.COMMAND_RUNTIME_PROTOCOL_VERSION, 1);
});
async function collect(stream) {
const chunks = [];
for await (const chunk of stream) chunks.push(Buffer.from(chunk));
return Buffer.concat(chunks);
}
function exited(child) {
return new Promise((resolve, reject) => {
child.once("error", reject);
child.once("exit", (code, signal) => resolve({ code, signal }));
});
}
function decodeEvents(bytes) {
const decoder = createCommandRuntimeMessageDecoder(CommandRuntimeProcessEventSchema);
const split = Math.min(13, bytes.length);
const events = [
...decoder.push(bytes.subarray(0, split)),
...decoder.push(bytes.subarray(split)),
];
decoder.finish();
return events;
}
function inject(value, nestingPath, field, injected) {
const copy = structuredClone(value);
let target = copy;
for (const segment of nestingPath) target = target[segment];
target[field] = injected;
return copy;
}