mirror of
https://github.com/clawdbot/clawdbot.git
synced 2026-02-01 03:47:45 +01:00
refactor(test): centralize temp home + polling
This commit is contained in:
@@ -30,18 +30,13 @@ vi.mock("../agents/model-catalog.js", () => ({
|
||||
async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||
return withTempHomeBase(
|
||||
async (home) => {
|
||||
const previousStateDir = process.env.CLAWDBOT_STATE_DIR;
|
||||
const previousAgentDir = process.env.CLAWDBOT_AGENT_DIR;
|
||||
const previousPiAgentDir = process.env.PI_CODING_AGENT_DIR;
|
||||
process.env.CLAWDBOT_STATE_DIR = path.join(home, ".clawdbot");
|
||||
process.env.CLAWDBOT_AGENT_DIR = path.join(home, ".clawdbot", "agent");
|
||||
process.env.PI_CODING_AGENT_DIR = process.env.CLAWDBOT_AGENT_DIR;
|
||||
try {
|
||||
return await fn(home);
|
||||
} finally {
|
||||
if (previousStateDir === undefined)
|
||||
delete process.env.CLAWDBOT_STATE_DIR;
|
||||
else process.env.CLAWDBOT_STATE_DIR = previousStateDir;
|
||||
if (previousAgentDir === undefined)
|
||||
delete process.env.CLAWDBOT_AGENT_DIR;
|
||||
else process.env.CLAWDBOT_AGENT_DIR = previousAgentDir;
|
||||
|
||||
@@ -55,22 +55,9 @@ vi.mock("../web/session.js", () => webMocks);
|
||||
async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||
return withTempHomeBase(
|
||||
async (home) => {
|
||||
const previousStateDir = process.env.CLAWDBOT_STATE_DIR;
|
||||
const previousClawdisStateDir = process.env.CLAWDIS_STATE_DIR;
|
||||
process.env.CLAWDBOT_STATE_DIR = join(home, ".clawdbot");
|
||||
process.env.CLAWDIS_STATE_DIR = join(home, ".clawdbot");
|
||||
try {
|
||||
vi.mocked(runEmbeddedPiAgent).mockClear();
|
||||
vi.mocked(abortEmbeddedPiRun).mockClear();
|
||||
return await fn(home);
|
||||
} finally {
|
||||
if (previousStateDir === undefined)
|
||||
delete process.env.CLAWDBOT_STATE_DIR;
|
||||
else process.env.CLAWDBOT_STATE_DIR = previousStateDir;
|
||||
if (previousClawdisStateDir === undefined)
|
||||
delete process.env.CLAWDIS_STATE_DIR;
|
||||
else process.env.CLAWDIS_STATE_DIR = previousClawdisStateDir;
|
||||
}
|
||||
vi.mocked(runEmbeddedPiAgent).mockClear();
|
||||
vi.mocked(abortEmbeddedPiRun).mockClear();
|
||||
return await fn(home);
|
||||
},
|
||||
{ prefix: "clawdbot-triggers-" },
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ import path from "node:path";
|
||||
|
||||
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { pollUntil } from "../../../test/helpers/poll.js";
|
||||
import { approveNodePairing, listNodePairing } from "../node-pairing.js";
|
||||
import { configureNodeBridgeSocket, startNodeBridgeServer } from "./server.js";
|
||||
|
||||
@@ -169,19 +170,16 @@ describe("node bridge server", () => {
|
||||
sendLine(socket, { type: "pair-request", nodeId: "n2", platform: "ios" });
|
||||
|
||||
// Approve the pending request from the gateway side.
|
||||
let reqId: string | undefined;
|
||||
for (let i = 0; i < 40; i += 1) {
|
||||
const list = await listNodePairing(baseDir);
|
||||
const req = list.pending.find((p) => p.nodeId === "n2");
|
||||
if (req) {
|
||||
reqId = req.requestId;
|
||||
break;
|
||||
}
|
||||
await new Promise((r) => setTimeout(r, 25));
|
||||
}
|
||||
expect(reqId).toBeTruthy();
|
||||
if (!reqId) throw new Error("expected a pending requestId");
|
||||
await approveNodePairing(reqId, baseDir);
|
||||
const pending = await pollUntil(
|
||||
async () => {
|
||||
const list = await listNodePairing(baseDir);
|
||||
return list.pending.find((p) => p.nodeId === "n2");
|
||||
},
|
||||
{ timeoutMs: 3000 },
|
||||
);
|
||||
expect(pending).toBeTruthy();
|
||||
if (!pending) throw new Error("expected a pending request");
|
||||
await approveNodePairing(pending.requestId, baseDir);
|
||||
|
||||
const line1 = JSON.parse(await readLine()) as {
|
||||
type: string;
|
||||
@@ -220,12 +218,10 @@ describe("node bridge server", () => {
|
||||
});
|
||||
|
||||
const socket = net.connect({ host: "127.0.0.1", port: server.port });
|
||||
await waitForSocketConnect(socket);
|
||||
sendLine(socket, { type: "pair-request", nodeId: "n3", platform: "ios" });
|
||||
|
||||
for (let i = 0; i < 40; i += 1) {
|
||||
if (requested) break;
|
||||
await new Promise((r) => setTimeout(r, 25));
|
||||
}
|
||||
await pollUntil(async () => requested, { timeoutMs: 3000 });
|
||||
|
||||
expect(requested?.nodeId).toBe("n3");
|
||||
expect(typeof requested?.requestId).toBe("string");
|
||||
@@ -258,19 +254,16 @@ describe("node bridge server", () => {
|
||||
});
|
||||
|
||||
// Approve the pending request from the gateway side.
|
||||
let reqId: string | undefined;
|
||||
for (let i = 0; i < 120; i += 1) {
|
||||
const list = await listNodePairing(baseDir);
|
||||
const req = list.pending.find((p) => p.nodeId === "n3-rpc");
|
||||
if (req) {
|
||||
reqId = req.requestId;
|
||||
break;
|
||||
}
|
||||
await new Promise((r) => setTimeout(r, 25));
|
||||
}
|
||||
expect(reqId).toBeTruthy();
|
||||
if (!reqId) throw new Error("expected a pending requestId");
|
||||
await approveNodePairing(reqId, baseDir);
|
||||
const pending = await pollUntil(
|
||||
async () => {
|
||||
const list = await listNodePairing(baseDir);
|
||||
return list.pending.find((p) => p.nodeId === "n3-rpc");
|
||||
},
|
||||
{ timeoutMs: 3000 },
|
||||
);
|
||||
expect(pending).toBeTruthy();
|
||||
if (!pending) throw new Error("expected a pending request");
|
||||
await approveNodePairing(pending.requestId, baseDir);
|
||||
|
||||
const line1 = JSON.parse(await readLine()) as { type: string };
|
||||
expect(line1.type).toBe("pair-ok");
|
||||
@@ -343,6 +336,7 @@ describe("node bridge server", () => {
|
||||
});
|
||||
|
||||
const socket = net.connect({ host: "127.0.0.1", port: server.port });
|
||||
await waitForSocketConnect(socket);
|
||||
const readLine = createLineReader(socket);
|
||||
sendLine(socket, {
|
||||
type: "pair-request",
|
||||
@@ -356,19 +350,16 @@ describe("node bridge server", () => {
|
||||
});
|
||||
|
||||
// Approve the pending request from the gateway side.
|
||||
let reqId: string | undefined;
|
||||
for (let i = 0; i < 40; i += 1) {
|
||||
const list = await listNodePairing(baseDir);
|
||||
const req = list.pending.find((p) => p.nodeId === "n4");
|
||||
if (req) {
|
||||
reqId = req.requestId;
|
||||
break;
|
||||
}
|
||||
await new Promise((r) => setTimeout(r, 25));
|
||||
}
|
||||
expect(reqId).toBeTruthy();
|
||||
if (!reqId) throw new Error("expected a pending requestId");
|
||||
const approved = await approveNodePairing(reqId, baseDir);
|
||||
const pending = await pollUntil(
|
||||
async () => {
|
||||
const list = await listNodePairing(baseDir);
|
||||
return list.pending.find((p) => p.nodeId === "n4");
|
||||
},
|
||||
{ timeoutMs: 3000 },
|
||||
);
|
||||
expect(pending).toBeTruthy();
|
||||
if (!pending) throw new Error("expected a pending request");
|
||||
const approved = await approveNodePairing(pending.requestId, baseDir);
|
||||
const token = approved?.node?.token ?? "";
|
||||
expect(token.length).toBeGreaterThan(0);
|
||||
|
||||
@@ -379,6 +370,7 @@ describe("node bridge server", () => {
|
||||
socket.destroy();
|
||||
|
||||
const socket2 = net.connect({ host: "127.0.0.1", port: server.port });
|
||||
await waitForSocketConnect(socket2);
|
||||
const readLine2 = createLineReader(socket2);
|
||||
sendLine(socket2, {
|
||||
type: "hello",
|
||||
@@ -394,10 +386,10 @@ describe("node bridge server", () => {
|
||||
const line3 = JSON.parse(await readLine2()) as { type: string };
|
||||
expect(line3.type).toBe("hello-ok");
|
||||
|
||||
for (let i = 0; i < 40; i += 1) {
|
||||
if (lastAuthed?.nodeId === "n4") break;
|
||||
await new Promise((r) => setTimeout(r, 25));
|
||||
}
|
||||
await pollUntil(
|
||||
async () => (lastAuthed?.nodeId === "n4" ? lastAuthed : null),
|
||||
{ timeoutMs: 3000 },
|
||||
);
|
||||
|
||||
expect(lastAuthed?.nodeId).toBe("n4");
|
||||
// Prefer paired metadata over hello payload (token verifies the stored node record).
|
||||
@@ -428,23 +420,21 @@ describe("node bridge server", () => {
|
||||
});
|
||||
|
||||
const socket = net.connect({ host: "127.0.0.1", port: server.port });
|
||||
await waitForSocketConnect(socket);
|
||||
const readLine = createLineReader(socket);
|
||||
sendLine(socket, { type: "pair-request", nodeId: "n5", platform: "ios" });
|
||||
|
||||
// Approve the pending request from the gateway side.
|
||||
let reqId: string | undefined;
|
||||
for (let i = 0; i < 40; i += 1) {
|
||||
const list = await listNodePairing(baseDir);
|
||||
const req = list.pending.find((p) => p.nodeId === "n5");
|
||||
if (req) {
|
||||
reqId = req.requestId;
|
||||
break;
|
||||
}
|
||||
await new Promise((r) => setTimeout(r, 25));
|
||||
}
|
||||
expect(reqId).toBeTruthy();
|
||||
if (!reqId) throw new Error("expected a pending requestId");
|
||||
await approveNodePairing(reqId, baseDir);
|
||||
const pending = await pollUntil(
|
||||
async () => {
|
||||
const list = await listNodePairing(baseDir);
|
||||
return list.pending.find((p) => p.nodeId === "n5");
|
||||
},
|
||||
{ timeoutMs: 3000 },
|
||||
);
|
||||
expect(pending).toBeTruthy();
|
||||
if (!pending) throw new Error("expected a pending request");
|
||||
await approveNodePairing(pending.requestId, baseDir);
|
||||
|
||||
const pairOk = JSON.parse(await readLine()) as {
|
||||
type: string;
|
||||
@@ -494,6 +484,7 @@ describe("node bridge server", () => {
|
||||
|
||||
// Ensure invoke works only for connected nodes (hello with token on a new socket).
|
||||
const socket2 = net.connect({ host: "127.0.0.1", port: server.port });
|
||||
await waitForSocketConnect(socket2);
|
||||
const readLine2 = createLineReader(socket2);
|
||||
sendLine(socket2, { type: "hello", nodeId: "n5", token });
|
||||
const hello2 = JSON.parse(await readLine2()) as { type: string };
|
||||
@@ -511,6 +502,7 @@ describe("node bridge server", () => {
|
||||
});
|
||||
|
||||
const socket = net.connect({ host: "127.0.0.1", port: server.port });
|
||||
await waitForSocketConnect(socket);
|
||||
const readLine = createLineReader(socket);
|
||||
sendLine(socket, {
|
||||
type: "pair-request",
|
||||
@@ -526,19 +518,16 @@ describe("node bridge server", () => {
|
||||
});
|
||||
|
||||
// Approve the pending request from the gateway side.
|
||||
let reqId: string | undefined;
|
||||
for (let i = 0; i < 40; i += 1) {
|
||||
const list = await listNodePairing(baseDir);
|
||||
const req = list.pending.find((p) => p.nodeId === "n-caps");
|
||||
if (req) {
|
||||
reqId = req.requestId;
|
||||
break;
|
||||
}
|
||||
await new Promise((r) => setTimeout(r, 25));
|
||||
}
|
||||
expect(reqId).toBeTruthy();
|
||||
if (!reqId) throw new Error("expected a pending requestId");
|
||||
await approveNodePairing(reqId, baseDir);
|
||||
const pending = await pollUntil(
|
||||
async () => {
|
||||
const list = await listNodePairing(baseDir);
|
||||
return list.pending.find((p) => p.nodeId === "n-caps");
|
||||
},
|
||||
{ timeoutMs: 3000 },
|
||||
);
|
||||
expect(pending).toBeTruthy();
|
||||
if (!pending) throw new Error("expected a pending request");
|
||||
await approveNodePairing(pending.requestId, baseDir);
|
||||
|
||||
const pairOk = JSON.parse(await readLine()) as { type: string };
|
||||
expect(pairOk.type).toBe("pair-ok");
|
||||
|
||||
25
test/helpers/poll.ts
Normal file
25
test/helpers/poll.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export type PollOptions = {
|
||||
timeoutMs?: number;
|
||||
intervalMs?: number;
|
||||
};
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise<void>((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export async function pollUntil<T>(
|
||||
fn: () => Promise<T | null | undefined>,
|
||||
opts: PollOptions = {},
|
||||
): Promise<T | undefined> {
|
||||
const timeoutMs = opts.timeoutMs ?? 2000;
|
||||
const intervalMs = opts.intervalMs ?? 25;
|
||||
const start = Date.now();
|
||||
|
||||
while (Date.now() - start < timeoutMs) {
|
||||
const value = await fn();
|
||||
if (value !== null && value !== undefined) return value;
|
||||
await sleep(intervalMs);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
@@ -7,6 +7,8 @@ type EnvSnapshot = {
|
||||
userProfile: string | undefined;
|
||||
homeDrive: string | undefined;
|
||||
homePath: string | undefined;
|
||||
stateDir: string | undefined;
|
||||
legacyStateDir: string | undefined;
|
||||
};
|
||||
|
||||
function snapshotEnv(): EnvSnapshot {
|
||||
@@ -15,6 +17,8 @@ function snapshotEnv(): EnvSnapshot {
|
||||
userProfile: process.env.USERPROFILE,
|
||||
homeDrive: process.env.HOMEDRIVE,
|
||||
homePath: process.env.HOMEPATH,
|
||||
stateDir: process.env.CLAWDBOT_STATE_DIR,
|
||||
legacyStateDir: process.env.CLAWDIS_STATE_DIR,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,11 +31,15 @@ function restoreEnv(snapshot: EnvSnapshot) {
|
||||
restoreKey("USERPROFILE", snapshot.userProfile);
|
||||
restoreKey("HOMEDRIVE", snapshot.homeDrive);
|
||||
restoreKey("HOMEPATH", snapshot.homePath);
|
||||
restoreKey("CLAWDBOT_STATE_DIR", snapshot.stateDir);
|
||||
restoreKey("CLAWDIS_STATE_DIR", snapshot.legacyStateDir);
|
||||
}
|
||||
|
||||
function setTempHome(base: string) {
|
||||
process.env.HOME = base;
|
||||
process.env.USERPROFILE = base;
|
||||
process.env.CLAWDBOT_STATE_DIR = path.join(base, ".clawdbot");
|
||||
process.env.CLAWDIS_STATE_DIR = path.join(base, ".clawdbot");
|
||||
|
||||
if (process.platform !== "win32") return;
|
||||
const match = base.match(/^([A-Za-z]:)(.*)$/);
|
||||
|
||||
Reference in New Issue
Block a user