test(agents): dedupe agent and cron test scaffolds

This commit is contained in:
Peter Steinberger
2026-03-02 06:40:42 +00:00
parent 281494ae52
commit 7e29d604ba
38 changed files with 3114 additions and 4486 deletions

View File

@@ -2,6 +2,28 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import type { SessionBindingRecord } from "../infra/outbound/session-binding-service.js";
function createDefaultSpawnConfig(): OpenClawConfig {
return {
acp: {
enabled: true,
backend: "acpx",
allowedAgents: ["codex"],
},
session: {
mainKey: "main",
scope: "per-sender",
},
channels: {
discord: {
threadBindings: {
enabled: true,
spawnAcpSessions: true,
},
},
},
};
}
const hoisted = vi.hoisted(() => {
const callGatewayMock = vi.fn();
const sessionBindingCapabilitiesMock = vi.fn();
@@ -12,25 +34,7 @@ const hoisted = vi.hoisted(() => {
const closeSessionMock = vi.fn();
const initializeSessionMock = vi.fn();
const state = {
cfg: {
acp: {
enabled: true,
backend: "acpx",
allowedAgents: ["codex"],
},
session: {
mainKey: "main",
scope: "per-sender",
},
channels: {
discord: {
threadBindings: {
enabled: true,
spawnAcpSessions: true,
},
},
},
} as OpenClawConfig,
cfg: createDefaultSpawnConfig(),
};
return {
callGatewayMock,
@@ -45,6 +49,27 @@ const hoisted = vi.hoisted(() => {
};
});
function buildSessionBindingServiceMock() {
return {
touch: vi.fn(),
bind(input: unknown) {
return hoisted.sessionBindingBindMock(input);
},
unbind(input: unknown) {
return hoisted.sessionBindingUnbindMock(input);
},
getCapabilities(params: unknown) {
return hoisted.sessionBindingCapabilitiesMock(params);
},
resolveByConversation(ref: unknown) {
return hoisted.sessionBindingResolveByConversationMock(ref);
},
listBySession(targetSessionKey: string) {
return hoisted.sessionBindingListBySessionMock(targetSessionKey);
},
};
}
vi.mock("../config/config.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../config/config.js")>();
return {
@@ -71,20 +96,21 @@ vi.mock("../infra/outbound/session-binding-service.js", async (importOriginal) =
await importOriginal<typeof import("../infra/outbound/session-binding-service.js")>();
return {
...actual,
getSessionBindingService: () => ({
bind: (input: unknown) => hoisted.sessionBindingBindMock(input),
getCapabilities: (params: unknown) => hoisted.sessionBindingCapabilitiesMock(params),
listBySession: (targetSessionKey: string) =>
hoisted.sessionBindingListBySessionMock(targetSessionKey),
resolveByConversation: (ref: unknown) => hoisted.sessionBindingResolveByConversationMock(ref),
touch: vi.fn(),
unbind: (input: unknown) => hoisted.sessionBindingUnbindMock(input),
}),
getSessionBindingService: () => buildSessionBindingServiceMock(),
};
});
const { spawnAcpDirect } = await import("./acp-spawn.js");
function createSessionBindingCapabilities() {
return {
adapterAvailable: true,
bindSupported: true,
unbindSupported: true,
placements: ["current", "child"] as const,
};
}
function createSessionBinding(overrides?: Partial<SessionBindingRecord>): SessionBindingRecord {
return {
bindingId: "default:child-thread",
@@ -106,27 +132,21 @@ function createSessionBinding(overrides?: Partial<SessionBindingRecord>): Sessio
};
}
function expectResolvedIntroTextInBindMetadata(): void {
const callWithMetadata = hoisted.sessionBindingBindMock.mock.calls.find(
(call: unknown[]) =>
typeof (call[0] as { metadata?: { introText?: unknown } } | undefined)?.metadata
?.introText === "string",
);
const introText =
(callWithMetadata?.[0] as { metadata?: { introText?: string } } | undefined)?.metadata
?.introText ?? "";
expect(introText.includes("session ids: pending (available after the first reply)")).toBe(false);
}
describe("spawnAcpDirect", () => {
beforeEach(() => {
hoisted.state.cfg = {
acp: {
enabled: true,
backend: "acpx",
allowedAgents: ["codex"],
},
session: {
mainKey: "main",
scope: "per-sender",
},
channels: {
discord: {
threadBindings: {
enabled: true,
spawnAcpSessions: true,
},
},
},
} satisfies OpenClawConfig;
hoisted.state.cfg = createDefaultSpawnConfig();
hoisted.callGatewayMock.mockReset().mockImplementation(async (argsUnknown: unknown) => {
const args = argsUnknown as { method?: string };
@@ -186,12 +206,9 @@ describe("spawnAcpDirect", () => {
};
});
hoisted.sessionBindingCapabilitiesMock.mockReset().mockReturnValue({
adapterAvailable: true,
bindSupported: true,
unbindSupported: true,
placements: ["current", "child"],
});
hoisted.sessionBindingCapabilitiesMock
.mockReset()
.mockReturnValue(createSessionBindingCapabilities());
hoisted.sessionBindingBindMock
.mockReset()
.mockImplementation(
@@ -248,15 +265,7 @@ describe("spawnAcpDirect", () => {
placement: "child",
}),
);
expect(hoisted.sessionBindingBindMock).toHaveBeenCalledWith(
expect.objectContaining({
metadata: expect.objectContaining({
introText: expect.not.stringContaining(
"session ids: pending (available after the first reply)",
),
}),
}),
);
expectResolvedIntroTextInBindMetadata();
const agentCall = hoisted.callGatewayMock.mock.calls
.map((call: unknown[]) => call[0] as { method?: string; params?: Record<string, unknown> })