| import { beforeEach, describe, expect, it, vi } from "vitest"; |
| import { |
| buildHandledBeforeAgentReplyPayloads, |
| runBeforeAgentReplyForTurn, |
| withBeforeAgentReplyObserver, |
| } from "./before-agent-reply.js"; |
|
|
| const hookRunner = vi.hoisted(() => ({ |
| hasHooks: vi.fn(), |
| runBeforeAgentReply: vi.fn(), |
| })); |
|
|
| vi.mock("./hook-runner-global.js", () => ({ |
| getGlobalHookRunner: () => hookRunner, |
| })); |
|
|
| function runHook(runId: string) { |
| return runBeforeAgentReplyForTurn({ |
| runId, |
| trigger: "user", |
| event: { cleanedBody: runId }, |
| context: { runId, trigger: "user" }, |
| }); |
| } |
|
|
| describe("before_agent_reply runner boundary", () => { |
| beforeEach(() => { |
| hookRunner.hasHooks.mockReset().mockReturnValue(true); |
| hookRunner.runBeforeAgentReply.mockReset().mockResolvedValue(undefined); |
| }); |
|
|
| it("preserves the complete reply payload", () => { |
| const reply = { |
| text: "claimed", |
| channelData: { native: true }, |
| sensitiveMedia: true, |
| videoAsNote: true, |
| }; |
|
|
| expect(buildHandledBeforeAgentReplyPayloads(reply)).toEqual([reply]); |
| }); |
|
|
| it.each([ |
| { runId: "missing", context: { runId: "missing" } }, |
| { runId: "mismatch", context: { runId: "mismatch", trigger: "heartbeat" } }, |
| ])("uses the validated turn trigger when context is $runId", async ({ runId, context }) => { |
| await runBeforeAgentReplyForTurn({ |
| runId, |
| trigger: "user", |
| event: { cleanedBody: runId }, |
| context, |
| }); |
|
|
| const expectedContext = { ...context, trigger: "user" }; |
| expect(hookRunner.hasHooks).toHaveBeenCalledWith("before_agent_reply", expectedContext); |
| expect(hookRunner.runBeforeAgentReply).toHaveBeenCalledWith( |
| { cleanedBody: runId }, |
| expectedContext, |
| ); |
| }); |
|
|
| it.each(["manual", "memory", "overflow"] as const)( |
| "does not dispatch for the internal %s trigger", |
| async (trigger) => { |
| await expect( |
| runBeforeAgentReplyForTurn({ |
| runId: trigger, |
| trigger, |
| event: { cleanedBody: trigger }, |
| context: { runId: trigger, trigger }, |
| }), |
| ).resolves.toBeUndefined(); |
|
|
| expect(hookRunner.hasHooks).not.toHaveBeenCalled(); |
| expect(hookRunner.runBeforeAgentReply).not.toHaveBeenCalled(); |
| }, |
| ); |
|
|
| it("keeps a nested run from checkpointing its parent admission", async () => { |
| const beforeDispatch = vi.fn(async () => undefined); |
| const afterDispatch = vi.fn(async (result) => result); |
| hookRunner.runBeforeAgentReply.mockImplementation(async (_event, context) => { |
| if (context.runId === "parent") { |
| await runHook("child"); |
| } |
| return undefined; |
| }); |
|
|
| await withBeforeAgentReplyObserver({ beforeDispatch, afterDispatch }, () => runHook("parent")); |
|
|
| expect(hookRunner.runBeforeAgentReply).toHaveBeenCalledTimes(2); |
| expect(beforeDispatch).toHaveBeenCalledOnce(); |
| expect(afterDispatch).toHaveBeenCalledOnce(); |
| }); |
| }); |
|
|