mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 11:51:37 +00:00
test(agents): dedupe agent and cron test scaffolds
This commit is contained in:
@@ -85,6 +85,7 @@ vi.mock("../../infra/outbound/session-binding-service.js", () => ({
|
||||
}));
|
||||
|
||||
const { tryDispatchAcpReply } = await import("./dispatch-acp.js");
|
||||
const sessionKey = "agent:codex-acp:session-1";
|
||||
|
||||
function createDispatcher(): {
|
||||
dispatcher: ReplyDispatcher;
|
||||
@@ -105,7 +106,7 @@ function createDispatcher(): {
|
||||
function setReadyAcpResolution() {
|
||||
managerMocks.resolveSession.mockReturnValue({
|
||||
kind: "ready",
|
||||
sessionKey: "agent:codex-acp:session-1",
|
||||
sessionKey,
|
||||
meta: createAcpSessionMeta(),
|
||||
});
|
||||
}
|
||||
@@ -124,6 +125,84 @@ function createAcpConfigWithVisibleToolTags(): OpenClawConfig {
|
||||
});
|
||||
}
|
||||
|
||||
async function runDispatch(params: {
|
||||
bodyForAgent: string;
|
||||
cfg?: OpenClawConfig;
|
||||
dispatcher?: ReplyDispatcher;
|
||||
shouldRouteToOriginating?: boolean;
|
||||
onReplyStart?: () => void;
|
||||
}) {
|
||||
return tryDispatchAcpReply({
|
||||
ctx: buildTestCtx({
|
||||
Provider: "discord",
|
||||
Surface: "discord",
|
||||
SessionKey: sessionKey,
|
||||
BodyForAgent: params.bodyForAgent,
|
||||
}),
|
||||
cfg: params.cfg ?? createAcpTestConfig(),
|
||||
dispatcher: params.dispatcher ?? createDispatcher().dispatcher,
|
||||
sessionKey,
|
||||
inboundAudio: false,
|
||||
shouldRouteToOriginating: params.shouldRouteToOriginating ?? false,
|
||||
...(params.shouldRouteToOriginating
|
||||
? { originatingChannel: "telegram", originatingTo: "telegram:thread-1" }
|
||||
: {}),
|
||||
shouldSendToolSummaries: true,
|
||||
bypassForCommand: false,
|
||||
...(params.onReplyStart ? { onReplyStart: params.onReplyStart } : {}),
|
||||
recordProcessed: vi.fn(),
|
||||
markIdle: vi.fn(),
|
||||
});
|
||||
}
|
||||
|
||||
async function emitToolLifecycleEvents(
|
||||
onEvent: (event: unknown) => Promise<void>,
|
||||
toolCallId: string,
|
||||
) {
|
||||
await onEvent({
|
||||
type: "tool_call",
|
||||
tag: "tool_call",
|
||||
toolCallId,
|
||||
status: "in_progress",
|
||||
title: "Run command",
|
||||
text: "Run command (in_progress)",
|
||||
});
|
||||
await onEvent({
|
||||
type: "tool_call",
|
||||
tag: "tool_call_update",
|
||||
toolCallId,
|
||||
status: "completed",
|
||||
title: "Run command",
|
||||
text: "Run command (completed)",
|
||||
});
|
||||
await onEvent({ type: "done" });
|
||||
}
|
||||
|
||||
function mockToolLifecycleTurn(toolCallId: string) {
|
||||
managerMocks.runTurn.mockImplementation(
|
||||
async ({ onEvent }: { onEvent: (event: unknown) => Promise<void> }) => {
|
||||
await emitToolLifecycleEvents(onEvent, toolCallId);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function mockVisibleTextTurn(text = "visible") {
|
||||
managerMocks.runTurn.mockImplementationOnce(
|
||||
async ({ onEvent }: { onEvent: (event: unknown) => Promise<void> }) => {
|
||||
await onEvent({ type: "text_delta", text, tag: "agent_message_chunk" });
|
||||
await onEvent({ type: "done" });
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async function dispatchVisibleTurn(onReplyStart: () => void) {
|
||||
await runDispatch({
|
||||
bodyForAgent: "visible",
|
||||
dispatcher: createDispatcher().dispatcher,
|
||||
onReplyStart,
|
||||
});
|
||||
}
|
||||
|
||||
describe("tryDispatchAcpReply", () => {
|
||||
beforeEach(() => {
|
||||
managerMocks.resolveSession.mockReset();
|
||||
@@ -160,24 +239,10 @@ describe("tryDispatchAcpReply", () => {
|
||||
);
|
||||
|
||||
const { dispatcher } = createDispatcher();
|
||||
const result = await tryDispatchAcpReply({
|
||||
ctx: buildTestCtx({
|
||||
Provider: "discord",
|
||||
Surface: "discord",
|
||||
SessionKey: "agent:codex-acp:session-1",
|
||||
BodyForAgent: "reply",
|
||||
}),
|
||||
cfg: createAcpTestConfig(),
|
||||
const result = await runDispatch({
|
||||
bodyForAgent: "reply",
|
||||
dispatcher,
|
||||
sessionKey: "agent:codex-acp:session-1",
|
||||
inboundAudio: false,
|
||||
shouldRouteToOriginating: true,
|
||||
originatingChannel: "telegram",
|
||||
originatingTo: "telegram:thread-1",
|
||||
shouldSendToolSummaries: true,
|
||||
bypassForCommand: false,
|
||||
recordProcessed: vi.fn(),
|
||||
markIdle: vi.fn(),
|
||||
});
|
||||
|
||||
expect(result?.counts.block).toBe(1);
|
||||
@@ -192,48 +257,15 @@ describe("tryDispatchAcpReply", () => {
|
||||
|
||||
it("edits ACP tool lifecycle updates in place when supported", async () => {
|
||||
setReadyAcpResolution();
|
||||
managerMocks.runTurn.mockImplementation(
|
||||
async ({ onEvent }: { onEvent: (event: unknown) => Promise<void> }) => {
|
||||
await onEvent({
|
||||
type: "tool_call",
|
||||
tag: "tool_call",
|
||||
toolCallId: "call-1",
|
||||
status: "in_progress",
|
||||
title: "Run command",
|
||||
text: "Run command (in_progress)",
|
||||
});
|
||||
await onEvent({
|
||||
type: "tool_call",
|
||||
tag: "tool_call_update",
|
||||
toolCallId: "call-1",
|
||||
status: "completed",
|
||||
title: "Run command",
|
||||
text: "Run command (completed)",
|
||||
});
|
||||
await onEvent({ type: "done" });
|
||||
},
|
||||
);
|
||||
mockToolLifecycleTurn("call-1");
|
||||
routeMocks.routeReply.mockResolvedValueOnce({ ok: true, messageId: "tool-msg-1" });
|
||||
|
||||
const { dispatcher } = createDispatcher();
|
||||
await tryDispatchAcpReply({
|
||||
ctx: buildTestCtx({
|
||||
Provider: "discord",
|
||||
Surface: "discord",
|
||||
SessionKey: "agent:codex-acp:session-1",
|
||||
BodyForAgent: "run tool",
|
||||
}),
|
||||
await runDispatch({
|
||||
bodyForAgent: "run tool",
|
||||
cfg: createAcpConfigWithVisibleToolTags(),
|
||||
dispatcher,
|
||||
sessionKey: "agent:codex-acp:session-1",
|
||||
inboundAudio: false,
|
||||
shouldRouteToOriginating: true,
|
||||
originatingChannel: "telegram",
|
||||
originatingTo: "telegram:thread-1",
|
||||
shouldSendToolSummaries: true,
|
||||
bypassForCommand: false,
|
||||
recordProcessed: vi.fn(),
|
||||
markIdle: vi.fn(),
|
||||
});
|
||||
|
||||
expect(routeMocks.routeReply).toHaveBeenCalledTimes(1);
|
||||
@@ -249,51 +281,18 @@ describe("tryDispatchAcpReply", () => {
|
||||
|
||||
it("falls back to new tool message when edit fails", async () => {
|
||||
setReadyAcpResolution();
|
||||
managerMocks.runTurn.mockImplementation(
|
||||
async ({ onEvent }: { onEvent: (event: unknown) => Promise<void> }) => {
|
||||
await onEvent({
|
||||
type: "tool_call",
|
||||
tag: "tool_call",
|
||||
toolCallId: "call-2",
|
||||
status: "in_progress",
|
||||
title: "Run command",
|
||||
text: "Run command (in_progress)",
|
||||
});
|
||||
await onEvent({
|
||||
type: "tool_call",
|
||||
tag: "tool_call_update",
|
||||
toolCallId: "call-2",
|
||||
status: "completed",
|
||||
title: "Run command",
|
||||
text: "Run command (completed)",
|
||||
});
|
||||
await onEvent({ type: "done" });
|
||||
},
|
||||
);
|
||||
mockToolLifecycleTurn("call-2");
|
||||
routeMocks.routeReply
|
||||
.mockResolvedValueOnce({ ok: true, messageId: "tool-msg-2" })
|
||||
.mockResolvedValueOnce({ ok: true, messageId: "tool-msg-2-fallback" });
|
||||
messageActionMocks.runMessageAction.mockRejectedValueOnce(new Error("edit unsupported"));
|
||||
|
||||
const { dispatcher } = createDispatcher();
|
||||
await tryDispatchAcpReply({
|
||||
ctx: buildTestCtx({
|
||||
Provider: "discord",
|
||||
Surface: "discord",
|
||||
SessionKey: "agent:codex-acp:session-1",
|
||||
BodyForAgent: "run tool",
|
||||
}),
|
||||
await runDispatch({
|
||||
bodyForAgent: "run tool",
|
||||
cfg: createAcpConfigWithVisibleToolTags(),
|
||||
dispatcher,
|
||||
sessionKey: "agent:codex-acp:session-1",
|
||||
inboundAudio: false,
|
||||
shouldRouteToOriginating: true,
|
||||
originatingChannel: "telegram",
|
||||
originatingTo: "telegram:thread-1",
|
||||
shouldSendToolSummaries: true,
|
||||
bypassForCommand: false,
|
||||
recordProcessed: vi.fn(),
|
||||
markIdle: vi.fn(),
|
||||
});
|
||||
|
||||
expect(messageActionMocks.runMessageAction).toHaveBeenCalledTimes(1);
|
||||
@@ -317,50 +316,15 @@ describe("tryDispatchAcpReply", () => {
|
||||
await onEvent({ type: "done" });
|
||||
},
|
||||
);
|
||||
await tryDispatchAcpReply({
|
||||
ctx: buildTestCtx({
|
||||
Provider: "discord",
|
||||
Surface: "discord",
|
||||
SessionKey: "agent:codex-acp:session-1",
|
||||
BodyForAgent: "hidden",
|
||||
}),
|
||||
cfg: createAcpTestConfig(),
|
||||
await runDispatch({
|
||||
bodyForAgent: "hidden",
|
||||
dispatcher,
|
||||
sessionKey: "agent:codex-acp:session-1",
|
||||
inboundAudio: false,
|
||||
shouldRouteToOriginating: false,
|
||||
shouldSendToolSummaries: true,
|
||||
bypassForCommand: false,
|
||||
onReplyStart,
|
||||
recordProcessed: vi.fn(),
|
||||
markIdle: vi.fn(),
|
||||
});
|
||||
expect(onReplyStart).toHaveBeenCalledTimes(1);
|
||||
|
||||
managerMocks.runTurn.mockImplementationOnce(
|
||||
async ({ onEvent }: { onEvent: (event: unknown) => Promise<void> }) => {
|
||||
await onEvent({ type: "text_delta", text: "visible", tag: "agent_message_chunk" });
|
||||
await onEvent({ type: "done" });
|
||||
},
|
||||
);
|
||||
await tryDispatchAcpReply({
|
||||
ctx: buildTestCtx({
|
||||
Provider: "discord",
|
||||
Surface: "discord",
|
||||
SessionKey: "agent:codex-acp:session-1",
|
||||
BodyForAgent: "visible",
|
||||
}),
|
||||
cfg: createAcpTestConfig(),
|
||||
dispatcher: createDispatcher().dispatcher,
|
||||
sessionKey: "agent:codex-acp:session-1",
|
||||
inboundAudio: false,
|
||||
shouldRouteToOriginating: false,
|
||||
shouldSendToolSummaries: true,
|
||||
bypassForCommand: false,
|
||||
onReplyStart,
|
||||
recordProcessed: vi.fn(),
|
||||
markIdle: vi.fn(),
|
||||
});
|
||||
mockVisibleTextTurn();
|
||||
await dispatchVisibleTurn(onReplyStart);
|
||||
expect(onReplyStart).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
@@ -368,31 +332,8 @@ describe("tryDispatchAcpReply", () => {
|
||||
setReadyAcpResolution();
|
||||
const onReplyStart = vi.fn();
|
||||
|
||||
managerMocks.runTurn.mockImplementationOnce(
|
||||
async ({ onEvent }: { onEvent: (event: unknown) => Promise<void> }) => {
|
||||
await onEvent({ type: "text_delta", text: "visible", tag: "agent_message_chunk" });
|
||||
await onEvent({ type: "done" });
|
||||
},
|
||||
);
|
||||
|
||||
await tryDispatchAcpReply({
|
||||
ctx: buildTestCtx({
|
||||
Provider: "discord",
|
||||
Surface: "discord",
|
||||
SessionKey: "agent:codex-acp:session-1",
|
||||
BodyForAgent: "visible",
|
||||
}),
|
||||
cfg: createAcpTestConfig(),
|
||||
dispatcher: createDispatcher().dispatcher,
|
||||
sessionKey: "agent:codex-acp:session-1",
|
||||
inboundAudio: false,
|
||||
shouldRouteToOriginating: false,
|
||||
shouldSendToolSummaries: true,
|
||||
bypassForCommand: false,
|
||||
onReplyStart,
|
||||
recordProcessed: vi.fn(),
|
||||
markIdle: vi.fn(),
|
||||
});
|
||||
mockVisibleTextTurn();
|
||||
await dispatchVisibleTurn(onReplyStart);
|
||||
|
||||
expect(onReplyStart).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
@@ -402,23 +343,10 @@ describe("tryDispatchAcpReply", () => {
|
||||
const onReplyStart = vi.fn();
|
||||
const { dispatcher } = createDispatcher();
|
||||
|
||||
await tryDispatchAcpReply({
|
||||
ctx: buildTestCtx({
|
||||
Provider: "discord",
|
||||
Surface: "discord",
|
||||
SessionKey: "agent:codex-acp:session-1",
|
||||
BodyForAgent: " ",
|
||||
}),
|
||||
cfg: createAcpTestConfig(),
|
||||
await runDispatch({
|
||||
bodyForAgent: " ",
|
||||
dispatcher,
|
||||
sessionKey: "agent:codex-acp:session-1",
|
||||
inboundAudio: false,
|
||||
shouldRouteToOriginating: false,
|
||||
shouldSendToolSummaries: true,
|
||||
bypassForCommand: false,
|
||||
onReplyStart,
|
||||
recordProcessed: vi.fn(),
|
||||
markIdle: vi.fn(),
|
||||
});
|
||||
|
||||
expect(managerMocks.runTurn).not.toHaveBeenCalled();
|
||||
@@ -432,22 +360,9 @@ describe("tryDispatchAcpReply", () => {
|
||||
);
|
||||
const { dispatcher } = createDispatcher();
|
||||
|
||||
await tryDispatchAcpReply({
|
||||
ctx: buildTestCtx({
|
||||
Provider: "discord",
|
||||
Surface: "discord",
|
||||
SessionKey: "agent:codex-acp:session-1",
|
||||
BodyForAgent: "test",
|
||||
}),
|
||||
cfg: createAcpTestConfig(),
|
||||
await runDispatch({
|
||||
bodyForAgent: "test",
|
||||
dispatcher,
|
||||
sessionKey: "agent:codex-acp:session-1",
|
||||
inboundAudio: false,
|
||||
shouldRouteToOriginating: false,
|
||||
shouldSendToolSummaries: true,
|
||||
bypassForCommand: false,
|
||||
recordProcessed: vi.fn(),
|
||||
markIdle: vi.fn(),
|
||||
});
|
||||
|
||||
expect(managerMocks.runTurn).not.toHaveBeenCalled();
|
||||
|
||||
Reference in New Issue
Block a user