mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
test(gateway): speed up slow e2e test setup
This commit is contained in:
@@ -91,9 +91,9 @@ async function ensureResponseConsumed(res: Response) {
|
||||
}
|
||||
|
||||
describe("OpenResponses HTTP API (e2e)", () => {
|
||||
it("rejects when disabled (default + config)", { timeout: 120_000 }, async () => {
|
||||
it("rejects when disabled (default + config)", { timeout: 30_000 }, async () => {
|
||||
const port = await getFreePort();
|
||||
const _server = await startServer(port);
|
||||
const server = await startServer(port);
|
||||
try {
|
||||
const res = await postResponses(port, {
|
||||
model: "openclaw",
|
||||
@@ -102,7 +102,7 @@ describe("OpenResponses HTTP API (e2e)", () => {
|
||||
expect(res.status).toBe(404);
|
||||
await ensureResponseConsumed(res);
|
||||
} finally {
|
||||
// shared server
|
||||
await server.close({ reason: "test done" });
|
||||
}
|
||||
|
||||
const disabledPort = await getFreePort();
|
||||
|
||||
@@ -20,17 +20,22 @@ installGatewayTestHooks({ scope: "suite" });
|
||||
|
||||
let server: Awaited<ReturnType<typeof startServerWithClient>>["server"];
|
||||
let ws: Awaited<ReturnType<typeof startServerWithClient>>["ws"];
|
||||
let sharedSessionStoreDir: string;
|
||||
let sharedSessionStorePath: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
const started = await startServerWithClient();
|
||||
server = started.server;
|
||||
ws = started.ws;
|
||||
await connectOk(ws);
|
||||
sharedSessionStoreDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-gw-session-"));
|
||||
sharedSessionStorePath = path.join(sharedSessionStoreDir, "sessions.json");
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
ws.close();
|
||||
await server.close();
|
||||
await fs.rm(sharedSessionStoreDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
const BASE_IMAGE_PNG =
|
||||
@@ -49,8 +54,7 @@ async function setTestSessionStore(params: {
|
||||
entries: Record<string, Record<string, unknown>>;
|
||||
agentId?: string;
|
||||
}) {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-gw-"));
|
||||
testState.sessionStorePath = path.join(dir, "sessions.json");
|
||||
testState.sessionStorePath = sharedSessionStorePath;
|
||||
await writeSessionStore({
|
||||
entries: params.entries,
|
||||
agentId: params.agentId,
|
||||
@@ -213,10 +217,7 @@ describe("gateway server agent", () => {
|
||||
|
||||
test("agent preserves spawnDepth on subagent sessions", async () => {
|
||||
setRegistry(defaultRegistry);
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-gw-"));
|
||||
const storePath = path.join(dir, "sessions.json");
|
||||
testState.sessionStorePath = storePath;
|
||||
await writeSessionStore({
|
||||
await setTestSessionStore({
|
||||
entries: {
|
||||
"agent:main:subagent:depth": {
|
||||
sessionId: "sess-sub-depth",
|
||||
@@ -234,7 +235,7 @@ describe("gateway server agent", () => {
|
||||
});
|
||||
expect(res.ok).toBe(true);
|
||||
|
||||
const raw = await fs.readFile(storePath, "utf-8");
|
||||
const raw = await fs.readFile(sharedSessionStorePath, "utf-8");
|
||||
const persisted = JSON.parse(raw) as Record<
|
||||
string,
|
||||
{ spawnDepth?: number; spawnedBy?: string }
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
installGatewayTestHooks({ scope: "suite" });
|
||||
|
||||
let startedServer: Awaited<ReturnType<typeof startServerWithClient>> | null = null;
|
||||
let sharedTempRoot: string;
|
||||
|
||||
function requireWs(): Awaited<ReturnType<typeof startServerWithClient>>["ws"] {
|
||||
if (!startedServer) {
|
||||
@@ -23,6 +24,7 @@ function requireWs(): Awaited<ReturnType<typeof startServerWithClient>>["ws"] {
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
sharedTempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-sessions-config-"));
|
||||
startedServer = await startServerWithClient(undefined, { controlUiEnabled: true });
|
||||
await connectOk(requireWs());
|
||||
});
|
||||
@@ -34,8 +36,16 @@ afterAll(async () => {
|
||||
startedServer.ws.close();
|
||||
await startedServer.server.close();
|
||||
startedServer = null;
|
||||
await fs.rm(sharedTempRoot, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
async function resetTempDir(name: string): Promise<string> {
|
||||
const dir = path.join(sharedTempRoot, name);
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
return dir;
|
||||
}
|
||||
|
||||
describe("gateway config methods", () => {
|
||||
it("rejects config.patch when raw is not an object", async () => {
|
||||
const res = await rpcReq<{ ok?: boolean }>(requireWs(), "config.patch", {
|
||||
@@ -48,7 +58,7 @@ describe("gateway config methods", () => {
|
||||
|
||||
describe("gateway server sessions", () => {
|
||||
it("filters sessions by agentId", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-sessions-agents-"));
|
||||
const dir = await resetTempDir("agents");
|
||||
testState.sessionConfig = {
|
||||
store: path.join(dir, "{agentId}", "sessions.json"),
|
||||
};
|
||||
@@ -109,7 +119,7 @@ describe("gateway server sessions", () => {
|
||||
});
|
||||
|
||||
it("resolves and patches main alias to default agent main key", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-sessions-"));
|
||||
const dir = await resetTempDir("main-alias");
|
||||
const storePath = path.join(dir, "sessions.json");
|
||||
testState.sessionStorePath = storePath;
|
||||
testState.agentsConfig = { list: [{ id: "ops", default: true }] };
|
||||
|
||||
@@ -7,10 +7,11 @@ import { startGatewayServerHarness, type GatewayServerHarness } from "./server.e
|
||||
import { installGatewayTestHooks, onceMessage } from "./test-helpers.js";
|
||||
|
||||
installGatewayTestHooks({ scope: "suite" });
|
||||
const HEALTH_E2E_TIMEOUT_MS = 30_000;
|
||||
const HEALTH_E2E_TIMEOUT_MS = 20_000;
|
||||
const PRESENCE_EVENT_TIMEOUT_MS = 6_000;
|
||||
const SHUTDOWN_EVENT_TIMEOUT_MS = 3_000;
|
||||
const FINGERPRINT_TIMEOUT_MS = 3_000;
|
||||
const CLI_PRESENCE_TIMEOUT_MS = 3_000;
|
||||
|
||||
let harness: GatewayServerHarness;
|
||||
|
||||
@@ -45,26 +46,19 @@ describe("gateway server health/presence", () => {
|
||||
ws,
|
||||
(o) => o.type === "res" && o.id === "presence1",
|
||||
);
|
||||
const channelsP = onceMessage<GatewayFrame>(
|
||||
ws,
|
||||
(o) => o.type === "res" && o.id === "channels1",
|
||||
);
|
||||
|
||||
const sendReq = (id: string, method: string) =>
|
||||
ws.send(JSON.stringify({ type: "req", id, method }));
|
||||
sendReq("health1", "health");
|
||||
sendReq("status1", "status");
|
||||
sendReq("presence1", "system-presence");
|
||||
sendReq("channels1", "channels.status");
|
||||
|
||||
const health = await healthP;
|
||||
const status = await statusP;
|
||||
const presence = await presenceP;
|
||||
const channels = await channelsP;
|
||||
expect(health.ok).toBe(true);
|
||||
expect(status.ok).toBe(true);
|
||||
expect(presence.ok).toBe(true);
|
||||
expect(channels.ok).toBe(true);
|
||||
expect(Array.isArray(presence.payload)).toBe(true);
|
||||
|
||||
ws.close();
|
||||
@@ -292,7 +286,7 @@ describe("gateway server health/presence", () => {
|
||||
const presenceP = onceMessage<GatewayFrame>(
|
||||
ws,
|
||||
(o) => o.type === "res" && o.id === "cli-presence",
|
||||
4000,
|
||||
CLI_PRESENCE_TIMEOUT_MS,
|
||||
);
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
|
||||
@@ -193,7 +193,7 @@ describe("gateway server models + voicewake", () => {
|
||||
|
||||
test(
|
||||
"voicewake.get returns defaults and voicewake.set broadcasts",
|
||||
{ timeout: 60_000 },
|
||||
{ timeout: 20_000 },
|
||||
async () => {
|
||||
await withTempHome(async (homeDir) => {
|
||||
const initial = await rpcReq<{ triggers: string[] }>(ws, "voicewake.get");
|
||||
@@ -379,7 +379,7 @@ describe("gateway server misc", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("send dedupes by idempotencyKey", { timeout: 60_000 }, async () => {
|
||||
test("send dedupes by idempotencyKey", { timeout: 15_000 }, async () => {
|
||||
const prevRegistry = getActivePluginRegistry() ?? emptyRegistry;
|
||||
try {
|
||||
setActivePluginRegistry(whatsappRegistry);
|
||||
@@ -452,8 +452,9 @@ describe("gateway server misc", () => {
|
||||
|
||||
test("refuses to start when port already bound", async () => {
|
||||
const { server: blocker, port: blockedPort } = await occupyPort();
|
||||
await expect(startGatewayServer(blockedPort)).rejects.toBeInstanceOf(GatewayLockError);
|
||||
await expect(startGatewayServer(blockedPort)).rejects.toThrow(/already listening/i);
|
||||
const startup = startGatewayServer(blockedPort);
|
||||
await expect(startup).rejects.toBeInstanceOf(GatewayLockError);
|
||||
await expect(startup).rejects.toThrow(/already listening/i);
|
||||
blocker.close();
|
||||
});
|
||||
|
||||
|
||||
@@ -93,22 +93,27 @@ vi.mock("../discord/monitor/thread-bindings.js", async (importOriginal) => {
|
||||
installGatewayTestHooks({ scope: "suite" });
|
||||
|
||||
let harness: GatewayServerHarness;
|
||||
let sharedSessionStoreDir: string;
|
||||
let sharedSessionStorePath: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
harness = await startGatewayServerHarness();
|
||||
sharedSessionStoreDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-sessions-"));
|
||||
sharedSessionStorePath = path.join(sharedSessionStoreDir, "sessions.json");
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await harness.close();
|
||||
await fs.rm(sharedSessionStoreDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
const openClient = async (opts?: Parameters<typeof connectOk>[1]) => await harness.openClient(opts);
|
||||
|
||||
async function createSessionStoreDir() {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-sessions-"));
|
||||
const storePath = path.join(dir, "sessions.json");
|
||||
testState.sessionStorePath = storePath;
|
||||
return { dir, storePath };
|
||||
await fs.rm(sharedSessionStoreDir, { recursive: true, force: true });
|
||||
await fs.mkdir(sharedSessionStoreDir, { recursive: true });
|
||||
testState.sessionStorePath = sharedSessionStorePath;
|
||||
return { dir: sharedSessionStoreDir, storePath: sharedSessionStorePath };
|
||||
}
|
||||
|
||||
async function writeSingleLineSession(dir: string, sessionId: string, content: string) {
|
||||
@@ -472,9 +477,7 @@ describe("gateway server sessions", () => {
|
||||
});
|
||||
|
||||
test("sessions.preview returns transcript previews", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-sessions-preview-"));
|
||||
const storePath = path.join(dir, "sessions.json");
|
||||
testState.sessionStorePath = storePath;
|
||||
const { dir } = await createSessionStoreDir();
|
||||
const sessionId = "sess-preview";
|
||||
const transcriptPath = path.join(dir, `${sessionId}.jsonl`);
|
||||
const lines = createToolSummaryPreviewTranscriptLines(sessionId);
|
||||
@@ -498,9 +501,7 @@ describe("gateway server sessions", () => {
|
||||
});
|
||||
|
||||
test("sessions.preview resolves legacy mixed-case main alias with custom mainKey", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-sessions-preview-alias-"));
|
||||
const storePath = path.join(dir, "sessions.json");
|
||||
testState.sessionStorePath = storePath;
|
||||
const { dir, storePath } = await createSessionStoreDir();
|
||||
testState.agentsConfig = { list: [{ id: "ops", default: true }] };
|
||||
testState.sessionConfig = { mainKey: "work" };
|
||||
const sessionId = "sess-legacy-main";
|
||||
@@ -533,9 +534,7 @@ describe("gateway server sessions", () => {
|
||||
});
|
||||
|
||||
test("sessions.resolve and mutators clean legacy main-alias ghost keys", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-sessions-cleanup-alias-"));
|
||||
const storePath = path.join(dir, "sessions.json");
|
||||
testState.sessionStorePath = storePath;
|
||||
const { dir, storePath } = await createSessionStoreDir();
|
||||
testState.agentsConfig = { list: [{ id: "ops", default: true }] };
|
||||
testState.sessionConfig = { mainKey: "work" };
|
||||
const sessionId = "sess-alias-cleanup";
|
||||
@@ -1044,9 +1043,7 @@ describe("gateway server sessions", () => {
|
||||
});
|
||||
|
||||
test("webchat clients cannot patch or delete sessions", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-sessions-webchat-"));
|
||||
const storePath = path.join(dir, "sessions.json");
|
||||
testState.sessionStorePath = storePath;
|
||||
await createSessionStoreDir();
|
||||
|
||||
await writeSessionStore({
|
||||
entries: {
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
loadOrCreateDeviceIdentity,
|
||||
publicKeyRawBase64UrlFromPem,
|
||||
signDevicePayload,
|
||||
} from "../infra/device-identity.js";
|
||||
import { buildDeviceAuthPayload } from "./device-auth.js";
|
||||
import {
|
||||
connectOk,
|
||||
installGatewayTestHooks,
|
||||
@@ -10,21 +18,16 @@ import { withServer } from "./test-with-server.js";
|
||||
installGatewayTestHooks({ scope: "suite" });
|
||||
|
||||
type GatewaySocket = Parameters<Parameters<typeof withServer>[0]>[0];
|
||||
const TALK_CONFIG_DEVICE_PATH = path.join(
|
||||
os.tmpdir(),
|
||||
`openclaw-talk-config-device-${process.pid}.json`,
|
||||
);
|
||||
const TALK_CONFIG_DEVICE = loadOrCreateDeviceIdentity(TALK_CONFIG_DEVICE_PATH);
|
||||
|
||||
async function createFreshOperatorDevice(scopes: string[], nonce: string) {
|
||||
const { randomUUID } = await import("node:crypto");
|
||||
const { tmpdir } = await import("node:os");
|
||||
const { join } = await import("node:path");
|
||||
const { buildDeviceAuthPayload } = await import("./device-auth.js");
|
||||
const { loadOrCreateDeviceIdentity, publicKeyRawBase64UrlFromPem, signDevicePayload } =
|
||||
await import("../infra/device-identity.js");
|
||||
|
||||
const identity = loadOrCreateDeviceIdentity(
|
||||
join(tmpdir(), `openclaw-talk-config-${randomUUID()}.json`),
|
||||
);
|
||||
const signedAtMs = Date.now();
|
||||
const payload = buildDeviceAuthPayload({
|
||||
deviceId: identity.deviceId,
|
||||
deviceId: TALK_CONFIG_DEVICE.deviceId,
|
||||
clientId: "test",
|
||||
clientMode: "test",
|
||||
role: "operator",
|
||||
@@ -35,9 +38,9 @@ async function createFreshOperatorDevice(scopes: string[], nonce: string) {
|
||||
});
|
||||
|
||||
return {
|
||||
id: identity.deviceId,
|
||||
publicKey: publicKeyRawBase64UrlFromPem(identity.publicKeyPem),
|
||||
signature: signDevicePayload(identity.privateKeyPem, payload),
|
||||
id: TALK_CONFIG_DEVICE.deviceId,
|
||||
publicKey: publicKeyRawBase64UrlFromPem(TALK_CONFIG_DEVICE.publicKeyPem),
|
||||
signature: signDevicePayload(TALK_CONFIG_DEVICE.privateKeyPem, payload),
|
||||
signedAt: signedAtMs,
|
||||
nonce,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user