Files
pi-gui/test/bridge-service.test.js

189 lines
5.2 KiB
JavaScript

import assert from "node:assert/strict";
import { EventEmitter } from "node:events";
import { mkdir, mkdtemp } from "node:fs/promises";
import { createConnection } from "node:net";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import { startBridgeService } from "../src/bridge/service.js";
function connect(socketPath) {
return new Promise((resolve, reject) => {
const socket = createConnection(socketPath);
socket.once("connect", () => resolve(socket));
socket.once("error", reject);
});
}
function request(socket, frame) {
return new Promise((resolve, reject) => {
let buffer = "";
const onError = (error) => {
socket.off("data", onData);
reject(error);
};
const onData = (chunk) => {
buffer += chunk;
const newline = buffer.indexOf("\n");
if (newline === -1) return;
socket.off("data", onData);
socket.off("error", onError);
try {
resolve(JSON.parse(buffer.slice(0, newline)));
} catch (error) {
reject(error);
}
};
socket.on("data", onData);
socket.once("error", onError);
socket.write(`${JSON.stringify(frame)}\n`);
});
}
function createAdapterFactory() {
const calls = [];
return {
calls,
startAdapter: (options) => {
const adapter = new EventEmitter();
adapter.sent = [];
adapter.send = async (command) => {
adapter.sent.push(command);
return {
type: "response",
command: command.type,
success: true,
data: { command },
};
};
adapter.respondToExtension = () => {};
adapter.stop = async () => {};
calls.push({ options, adapter });
return adapter;
},
};
}
test("starts the home agent and dispatches local protocol requests to the selected worktree", async () => {
const root = await mkdtemp(join(tmpdir(), "pi-status-bridge-service-"));
const home = join(root, "home");
const feature = join(root, "feature");
await Promise.all([mkdir(home), mkdir(feature)]);
const fixture = createAdapterFactory();
const service = await startBridgeService({
homeWorktree: home,
runtimeDir: join(root, "runtime"),
sessionRoot: join(root, "sessions"),
startAdapter: fixture.startAdapter,
});
const socket = await connect(service.socketPath);
try {
const agents = await request(socket, {
version: "v1",
id: "list-1",
op: "list_agents",
});
assert.equal(agents.result.agents.length, 1);
assert.equal(agents.result.agents[0].worktreePath, home);
const selected = await request(socket, {
version: "v1",
id: "select-1",
op: "select_agent",
payload: { worktreePath: feature },
});
const featureAgent = selected.result.agent;
const workspace = await request(socket, {
version: "v1",
id: "workspace-1",
op: "get_workspace",
});
const summary = await request(socket, {
version: "v1",
id: "summary-1",
op: "get_workspace_summary",
});
assert.equal(typeof workspace.result.bridgeInstanceId, "string");
assert.equal(workspace.result.directories.length, 2);
assert.equal(summary.result.openCount, 2);
assert.equal(summary.result.directoryCount, 2);
assert.equal(summary.result.resourceWarning, false);
const snapshot = await request(socket, {
version: "v1",
id: "snapshot-1",
op: "get_session_runtime_snapshot",
payload: { runtimeId: featureAgent.runtimeId },
});
assert.equal(snapshot.result.runtime.runtimeId, featureAgent.runtimeId);
assert.equal(
snapshot.result.bridgeInstanceId,
workspace.result.bridgeInstanceId,
);
const prompt = await request(socket, {
version: "v1",
id: "prompt-1",
op: "prompt",
agentId: featureAgent.id,
payload: { message: "Use this worktree" },
});
assert.equal(prompt.ok, true);
const named = await request(socket, {
version: "v1",
id: "name-1",
op: "set_session_name",
agentId: featureAgent.id,
payload: { name: "Feature work" },
});
const compacted = await request(socket, {
version: "v1",
id: "compact-1",
op: "compact",
agentId: featureAgent.id,
payload: { customInstructions: "Preserve tests" },
});
assert.equal(named.ok, true);
assert.equal(compacted.ok, true);
assert.deepEqual(
fixture.calls.map(({ options }) => options.cwd),
[home, feature],
);
assert.deepEqual(fixture.calls[1].adapter.sent, [
{ type: "get_state" },
{ type: "get_state" },
{ type: "get_session_stats" },
{ type: "get_messages" },
{ type: "get_commands" },
{ type: "get_available_models" },
{ type: "prompt", message: "Use this worktree" },
{ type: "set_session_name", name: "Feature work" },
{ type: "compact", customInstructions: "Preserve tests" },
]);
fixture.calls[1].options.onEvent({
type: "agent_state",
data: { state: "idle" },
});
const closed = await request(socket, {
version: "v1",
id: "close-1",
op: "close_session_runtime",
payload: { runtimeId: featureAgent.runtimeId },
});
assert.equal(closed.ok, true);
const forgotten = await request(socket, {
version: "v1",
id: "forget-1",
op: "forget_directory",
payload: { worktreePath: feature },
});
assert.equal(forgotten.ok, true);
assert.equal(forgotten.result.worktreePath, feature);
assert.equal((await service.listAgents()).length, 1);
} finally {
socket.destroy();
await service.close();
}
});