Files
fathah--hermes-desktop/tests/office-start.test.ts
T
2026-07-13 13:36:53 +08:00

195 lines
5.0 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
startOfficeStack,
type OfficeStartDependencies,
} from "../src/main/office-start";
import type { ConnectionConfig } from "../src/main/config";
function localConnection(): ConnectionConfig {
return {
mode: "local",
remoteUrl: "",
apiKey: "",
remoteChatTransport: "auto",
sshChatTransport: "auto",
ssh: {
host: "",
port: 22,
username: "",
keyPath: "",
remotePort: 8642,
localPort: 18642,
},
};
}
function sshConnection(): ConnectionConfig {
return {
...localConnection(),
mode: "ssh",
ssh: {
host: "office.example.com",
port: 22,
username: "hermes",
keyPath: "C:\\keys\\id_rsa",
remotePort: 8642,
localPort: 18642,
},
};
}
function makeDeps(
connection: ConnectionConfig,
overrides: Partial<OfficeStartDependencies> = {},
): { calls: string[]; deps: OfficeStartDependencies } {
const calls: string[] = [];
const deps: OfficeStartDependencies = {
getConnectionConfig: () => connection,
isGatewayRunning: () => false,
startGateway: (profile) => {
calls.push(`startGateway:${profile ?? ""}`);
return true;
},
sshGatewayStatus: async () => false,
sshStartGateway: async () => {
calls.push("sshStartGateway");
},
startSshTunnel: async () => {
calls.push("startSshTunnel");
},
stopSshTunnel: () => {
calls.push("stopSshTunnel");
},
sshReadRemoteApiKey: async () => "remote-key",
setSshRemoteApiKey: (key) => {
calls.push(`setSshRemoteApiKey:${key}`);
},
startClaw3dAll: () => {
calls.push("startClaw3dAll");
return { success: true };
},
stopClaw3dAll: () => {
calls.push("stopClaw3dAll");
},
waitForClaw3dReady: async () => {
calls.push("waitForClaw3dReady");
return true;
},
...overrides,
};
return { calls, deps };
}
describe("startOfficeStack", () => {
it("starts the local gateway with the active profile before Claw3D", async () => {
const { calls, deps } = makeDeps(localConnection());
const result = await startOfficeStack("research", deps);
expect(result).toEqual({ success: true });
expect(calls).toEqual([
"startGateway:research",
"startClaw3dAll",
"waitForClaw3dReady",
]);
});
it("does not restart a local gateway that is already running", async () => {
const { calls, deps } = makeDeps(localConnection(), {
isGatewayRunning: () => true,
});
const result = await startOfficeStack("research", deps);
expect(result).toEqual({ success: true });
expect(calls).toEqual(["startClaw3dAll", "waitForClaw3dReady"]);
});
it("starts the SSH gateway and tunnel before Claw3D", async () => {
const { calls, deps } = makeDeps(sshConnection());
const result = await startOfficeStack("research", deps);
expect(result).toEqual({ success: true });
expect(calls).toEqual([
"sshStartGateway",
"startSshTunnel",
"setSshRemoteApiKey:remote-key",
"startClaw3dAll",
"waitForClaw3dReady",
]);
});
it("returns an error when local Office does not become ready", async () => {
const { calls, deps } = makeDeps(localConnection(), {
waitForClaw3dReady: async () => {
calls.push("waitForClaw3dReady");
return false;
},
});
const result = await startOfficeStack("research", deps);
expect(result).toEqual({
success: false,
error:
"Office started but did not become ready in time. Check Office logs and try again.",
});
expect(calls).toEqual([
"startGateway:research",
"startClaw3dAll",
"waitForClaw3dReady",
"stopClaw3dAll",
]);
});
it("returns an error when SSH Office does not become ready", async () => {
const { calls, deps } = makeDeps(sshConnection(), {
waitForClaw3dReady: async () => {
calls.push("waitForClaw3dReady");
return false;
},
});
const result = await startOfficeStack("research", deps);
expect(result).toEqual({
success: false,
error:
"Office started but did not become ready in time. Check Office logs and try again.",
});
expect(calls).toEqual([
"sshStartGateway",
"startSshTunnel",
"setSshRemoteApiKey:remote-key",
"startClaw3dAll",
"waitForClaw3dReady",
"stopClaw3dAll",
"stopSshTunnel",
]);
});
it("stops the SSH tunnel when setup fails after the tunnel is open", async () => {
const { calls, deps } = makeDeps(sshConnection(), {
sshReadRemoteApiKey: async () => {
calls.push("sshReadRemoteApiKey");
throw new Error("remote key missing");
},
});
const result = await startOfficeStack("research", deps);
expect(result).toEqual({
success: false,
error: "remote key missing",
});
expect(calls).toEqual([
"sshStartGateway",
"startSshTunnel",
"sshReadRemoteApiKey",
"stopSshTunnel",
]);
});
});