Files

450 lines
15 KiB
JavaScript

import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import {
commandMatchesPackagedSidecar,
createRuntimeManager,
embeddedServerImportUrl,
migrateOpenworkServerTokenStore,
prepareRuntimeWorkspaceRoot,
prioritizeWorkspacePaths,
resetRuntimeStatesAfterFailedServerStart,
resolveEvalLocalServerDelayMs,
resolveOpenworkServerConfigPath,
resolveOpenworkServerReuse,
seedWorkspacePathsForEmbeddedServer,
selectStickyOpenworkPortWorkspace,
snapshotEngineState,
snapshotOpenworkServerState,
} from "./runtime.mjs";
describe("workspace root preparation", () => {
it("reports an inaccessible Windows drive as a controlled recoverable error", async () => {
const mkdirError = Object.assign(new Error("drive is unavailable"), { code: "ENOENT" });
let attemptedPath = null;
await assert.rejects(
prepareRuntimeWorkspaceRoot("\\\\?\\Z:\\Disconnected\\Workspace", {
platform: "win32",
mkdirImpl: async (workspaceRoot) => {
attemptedPath = workspaceRoot;
throw mkdirError;
},
}),
(error) => {
assert.ok(error instanceof Error);
assert.ok("code" in error);
assert.ok("workspacePath" in error);
assert.equal(error.code, "workspace_inaccessible");
assert.equal(error.workspacePath, "\\\\?\\Z:\\Disconnected\\Workspace");
assert.equal(error.cause, mkdirError);
return true;
},
);
assert.equal(attemptedPath, "Z:\\Disconnected\\Workspace");
});
it("returns the runtime lifecycle to idle after root preparation fails", async () => {
const root = await mkdtemp(path.join(os.tmpdir(), "openwork-runtime-root-"));
try {
const manager = createRuntimeManager({
app: {
getPath: (name) => name === "exe" ? path.join(root, "OpenWork.exe") : root,
isPackaged: false,
},
desktopRoot: path.dirname(fileURLToPath(import.meta.url)),
listLocalWorkspacePaths: async () => [],
localManagedMcpVaultKey: "test-key",
workspaceMkdir: async () => {
throw Object.assign(new Error("network share disconnected"), { code: "ENOENT" });
},
workspacePlatform: "win32",
});
await assert.rejects(
manager.engineStart("\\\\server\\share\\Workspace"),
(error) => error instanceof Error && "code" in error && error.code === "workspace_inaccessible",
);
const status = await manager.runtimeStatus();
assert.equal(status.lifecycleState, "idle");
assert.equal(status.engine.running, false);
assert.equal(status.engine.projectDir, null);
assert.equal(status.openworkServer.running, false);
} finally {
await rm(root, { recursive: true, force: true });
}
});
});
describe("bundled OpenCode runtime", () => {
it("pins the engine release containing the timestamp-based session loop repair", async () => {
const constantsPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../constants.json");
const constants = JSON.parse(await readFile(constantsPath, "utf8"));
// OpenCode #40990 stops old assistant messages with lexicographically
// later IDs from short-circuiting a newly appended user turn.
assert.equal(constants.opencodeVersion, "v1.18.18");
});
});
describe("openwork server snapshot", () => {
it("reports a running in-process server", () => {
const snapshot = snapshotOpenworkServerState({
child: null,
childExited: true,
inProcess: true,
});
assert.equal(snapshot.running, true);
});
});
describe("resolveOpenworkServerReuse", () => {
const healthy = {
forceRestart: undefined,
inProcess: true,
lifecycleState: "healthy",
remoteAccessEnabled: false,
requestedRemoteAccess: false,
currentProjectDir: "/Users/person/workspace-a",
requestedProjectDir: "/Users/person/workspace-a",
platform: "darwin",
};
it("reuses the running server for the same workspace", () => {
assert.deepEqual(resolveOpenworkServerReuse(healthy), { reuse: true, retarget: false });
});
it("retargets instead of restarting when a different workspace is requested", () => {
// Regression: a workspace switch (e.g. opening Settings while another
// workspace is routed) used to tear the server down here, aborting every
// in-flight run in the workspace being left.
assert.deepEqual(
resolveOpenworkServerReuse({ ...healthy, requestedProjectDir: "/Users/person/workspace-b" }),
{ reuse: true, retarget: true },
);
});
it("treats case-only path differences as the same workspace on win32", () => {
assert.deepEqual(
resolveOpenworkServerReuse({
...healthy,
platform: "win32",
currentProjectDir: "C:\\Work\\Space",
requestedProjectDir: "c:\\work\\space",
}),
{ reuse: true, retarget: false },
);
});
it("gives up the server only for an explicit restart, host rebind, or unhealthy runtime", () => {
assert.deepEqual(
resolveOpenworkServerReuse({ ...healthy, forceRestart: true }),
{ reuse: false, retarget: false },
);
assert.deepEqual(
resolveOpenworkServerReuse({ ...healthy, requestedRemoteAccess: true }),
{ reuse: false, retarget: false },
);
assert.deepEqual(
resolveOpenworkServerReuse({ ...healthy, lifecycleState: "starting" }),
{ reuse: false, retarget: false },
);
assert.deepEqual(
resolveOpenworkServerReuse({ ...healthy, inProcess: false }),
{ reuse: false, retarget: false },
);
});
});
describe("prioritizeWorkspacePaths", () => {
it("keeps the active runtime workspace first", () => {
assert.deepEqual(
prioritizeWorkspacePaths("/workspace/current", ["/workspace/other", "/workspace/current"]),
["/workspace/current", "/workspace/other"],
);
});
it("dedupes equivalent paths", () => {
assert.deepEqual(
prioritizeWorkspacePaths("/workspace/current/../current", ["/workspace/current"]),
["/workspace/current/../current"],
);
});
});
describe("seedWorkspacePathsForEmbeddedServer", () => {
it("uses persisted server config instead of Electron workspace state once config exists", () => {
assert.deepEqual(
seedWorkspacePathsForEmbeddedServer(["/workspace/legacy"], true),
[],
);
});
it("seeds from Electron workspace state before server config exists", () => {
assert.deepEqual(
seedWorkspacePathsForEmbeddedServer(["/workspace/first"], false),
["/workspace/first"],
);
});
});
describe("selectStickyOpenworkPortWorkspace", () => {
it("uses the requested workspace even when server config owns workspace loading", () => {
assert.equal(
selectStickyOpenworkPortWorkspace(["/workspace/current"], []),
"/workspace/current",
);
});
it("falls back to server workspace paths when no requested path is available", () => {
assert.equal(
selectStickyOpenworkPortWorkspace([], ["/workspace/from-server"]),
"/workspace/from-server",
);
});
});
describe("resolveEvalLocalServerDelayMs", () => {
it("enables only positive finite eval delays", () => {
assert.equal(resolveEvalLocalServerDelayMs({ OPENWORK_EVAL_LOCAL_SERVER_DELAY_MS: "3000" }), 3000);
assert.equal(resolveEvalLocalServerDelayMs({ OPENWORK_EVAL_LOCAL_SERVER_DELAY_MS: "0" }), 0);
assert.equal(resolveEvalLocalServerDelayMs({ OPENWORK_EVAL_LOCAL_SERVER_DELAY_MS: "-1" }), 0);
assert.equal(resolveEvalLocalServerDelayMs({ OPENWORK_EVAL_LOCAL_SERVER_DELAY_MS: "Infinity" }), 0);
assert.equal(resolveEvalLocalServerDelayMs({ OPENWORK_EVAL_LOCAL_SERVER_DELAY_MS: "invalid" }), 0);
});
});
describe("commandMatchesPackagedSidecar", () => {
it("matches packaged opencode sidecars with platform suffixes", () => {
assert.equal(
commandMatchesPackagedSidecar(
"/Applications/OpenWork.app/Contents/Resources/sidecars/opencode-aarch64-apple-darwin serve --hostname 127.0.0.1 --port 49174 --cors *",
["/Applications/OpenWork.app/Contents/Resources/sidecars"],
),
true,
);
});
it("does not match unrelated opencode processes outside sidecar directories", () => {
assert.equal(
commandMatchesPackagedSidecar(
"/usr/local/bin/opencode serve --hostname 127.0.0.1 --port 49174",
["/Applications/OpenWork.app/Contents/Resources/sidecars"],
),
false,
);
});
});
describe("embeddedServerImportUrl", () => {
it("returns the same file URL for unchanged metadata", async () => {
const dir = await mkdtemp(path.join(os.tmpdir(), "openwork-runtime-"));
try {
const embeddedPath = path.join(dir, "embedded.js");
await writeFile(embeddedPath, "export const value = 1;\n");
const first = embeddedServerImportUrl(embeddedPath);
const second = embeddedServerImportUrl(embeddedPath);
const url = new URL(first);
assert.equal(first, second);
assert.equal(url.protocol, "file:");
assert.equal(fileURLToPath(url), embeddedPath);
assert.ok(url.searchParams.get("mtimeMs"));
assert.equal(url.searchParams.get("size"), String("export const value = 1;\n".length));
} finally {
await rm(dir, { recursive: true, force: true });
}
});
it("changes when the file metadata changes", async () => {
const dir = await mkdtemp(path.join(os.tmpdir(), "openwork-runtime-"));
try {
const embeddedPath = path.join(dir, "embedded.js");
await writeFile(embeddedPath, "export const value = 1;\n");
const first = embeddedServerImportUrl(embeddedPath);
await writeFile(embeddedPath, "export const value = 12;\n");
assert.notEqual(embeddedServerImportUrl(embeddedPath), first);
} finally {
await rm(dir, { recursive: true, force: true });
}
});
it("falls back to the plain file URL if stat fails", () => {
const missingPath = path.join(os.tmpdir(), "openwork-missing-embedded.js");
assert.equal(embeddedServerImportUrl(missingPath), pathToFileURL(missingPath).href);
});
});
describe("resolveOpenworkServerConfigPath", () => {
it("respects explicit server config path", () => {
assert.equal(
resolveOpenworkServerConfigPath({ OPENWORK_SERVER_CONFIG: "/tmp/openwork/server.json" }),
"/tmp/openwork/server.json",
);
});
it("uses XDG config home on Unix", () => {
if (process.platform === "win32") return;
assert.equal(
resolveOpenworkServerConfigPath({ XDG_CONFIG_HOME: "/tmp/xdg" }),
"/tmp/xdg/openwork/server.json",
);
});
});
describe("OpenWork server credential persistence", () => {
it("deterministically migrates legacy workspace credentials into one server bundle", () => {
const migrated = migrateOpenworkServerTokenStore({
version: 1,
workspaces: {
"/workspace/z": {
clientToken: "client-z",
hostToken: "host-z",
ownerToken: "owner-z",
updatedAt: 20,
},
"/workspace/a": {
clientToken: "client-a",
hostToken: "host-a",
ownerToken: "owner-a",
updatedAt: 20,
},
"/workspace/old": {
clientToken: "client-old",
hostToken: "host-old",
ownerToken: "owner-old",
updatedAt: 10,
},
},
});
assert.deepEqual(migrated, {
version: 2,
credentials: {
clientToken: "client-a",
hostToken: "host-a",
ownerToken: "owner-a",
updatedAt: 20,
},
});
assert.deepEqual(migrateOpenworkServerTokenStore(migrated), migrated);
});
});
describe("snapshotEngineState", () => {
it("reports server-managed OpenCode liveness and pid without a child handle", () => {
const snapshot = snapshotEngineState({
child: null,
childExited: false,
runtime: "direct",
projectDir: "/workspace/current",
hostname: "127.0.0.1",
port: 4097,
baseUrl: "http://127.0.0.1:4097",
opencodeUsername: null,
opencodePassword: null,
opencodeBinPath: null,
opencodeBinSource: null,
managedByServer: true,
managedPid: 12345,
managedIsAlive: () => true,
lastStdout: null,
lastStderr: null,
execution: null,
});
assert.equal(snapshot.running, true);
assert.equal(snapshot.managedByServer, true);
assert.equal(snapshot.pid, 12345);
});
});
describe("resetRuntimeStatesAfterFailedServerStart", () => {
function staleServerState() {
return {
child: null,
childExited: true,
inProcess: true,
remoteAccessEnabled: true,
host: "127.0.0.1",
port: 4141,
baseUrl: "http://127.0.0.1:4141",
connectUrl: null,
mdnsUrl: null,
lanUrl: null,
clientToken: "client-token",
ownerToken: "owner-token",
hostToken: "host-token",
managedOpencodeBinPath: "/usr/local/bin/opencode",
managedOpencodeBinSource: "known-location",
lastStdout: "server stdout",
lastStderr: "server stderr",
managedOpencodeExecution: { command: "opencode" },
};
}
function staleEngineState() {
return {
child: null,
childExited: false,
runtime: "direct",
projectDir: "/workspace/current",
hostname: "127.0.0.1",
port: 4097,
baseUrl: "http://127.0.0.1:4097",
opencodeUsername: "user",
opencodePassword: "pass",
opencodeBinPath: "/usr/local/bin/opencode",
opencodeBinSource: "known-location",
managedByServer: true,
managedPid: 12345,
managedIsAlive: () => true,
lastStdout: "engine stdout",
lastStderr: "engine stderr",
execution: { command: "opencode" },
};
}
it("clears a dead managed runtime so snapshots cannot report it running", () => {
const serverState = staleServerState();
const engineState = staleEngineState();
resetRuntimeStatesAfterFailedServerStart(serverState, engineState, { manageOpencode: true });
assert.equal(serverState.inProcess, false);
assert.equal(serverState.port, null);
assert.equal(serverState.baseUrl, null);
assert.equal(serverState.ownerToken, null);
// Diagnostics survive the reset.
assert.equal(serverState.lastStdout, "server stdout");
assert.equal(serverState.lastStderr, "server stderr");
assert.equal(engineState.baseUrl, null);
assert.equal(engineState.managedByServer, false);
assert.equal(engineState.managedPid, null);
assert.equal(snapshotEngineState(engineState).running, false);
// A retry via engineRestart still knows its workspace.
assert.equal(engineState.projectDir, "/workspace/current");
assert.equal(engineState.lastStderr, "engine stderr");
});
it("leaves an external engine untouched when the failed start did not manage it", () => {
const serverState = staleServerState();
const engineState = staleEngineState();
engineState.managedByServer = false;
resetRuntimeStatesAfterFailedServerStart(serverState, engineState, { manageOpencode: false });
assert.equal(serverState.inProcess, false);
assert.equal(engineState.baseUrl, "http://127.0.0.1:4097");
assert.equal(engineState.projectDir, "/workspace/current");
});
});