mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-18 13:30:48 +00:00
refactor(hook-tests): share subagent hook helpers
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/discord";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
getRequiredHookHandler,
|
||||
registerHookHandlersForTest,
|
||||
} from "../../test-utils/subagent-hooks.js";
|
||||
import { registerDiscordSubagentHooks } from "./subagent-hooks.js";
|
||||
|
||||
type ThreadBindingRecord = {
|
||||
@@ -55,26 +59,10 @@ function registerHandlersForTest(
|
||||
},
|
||||
},
|
||||
) {
|
||||
const handlers = new Map<string, (event: unknown, ctx: unknown) => unknown>();
|
||||
const api = {
|
||||
return registerHookHandlersForTest<OpenClawPluginApi>({
|
||||
config,
|
||||
on: (hookName: string, handler: (event: unknown, ctx: unknown) => unknown) => {
|
||||
handlers.set(hookName, handler);
|
||||
},
|
||||
} as unknown as OpenClawPluginApi;
|
||||
registerDiscordSubagentHooks(api);
|
||||
return handlers;
|
||||
}
|
||||
|
||||
function getRequiredHandler(
|
||||
handlers: Map<string, (event: unknown, ctx: unknown) => unknown>,
|
||||
hookName: string,
|
||||
): (event: unknown, ctx: unknown) => unknown {
|
||||
const handler = handlers.get(hookName);
|
||||
if (!handler) {
|
||||
throw new Error(`expected ${hookName} hook handler`);
|
||||
}
|
||||
return handler;
|
||||
register: registerDiscordSubagentHooks,
|
||||
});
|
||||
}
|
||||
|
||||
function resolveSubagentDeliveryTargetForTest(requesterOrigin: {
|
||||
@@ -84,7 +72,7 @@ function resolveSubagentDeliveryTargetForTest(requesterOrigin: {
|
||||
threadId?: string;
|
||||
}) {
|
||||
const handlers = registerHandlersForTest();
|
||||
const handler = getRequiredHandler(handlers, "subagent_delivery_target");
|
||||
const handler = getRequiredHookHandler(handlers, "subagent_delivery_target");
|
||||
return handler(
|
||||
{
|
||||
childSessionKey: "agent:main:subagent:child",
|
||||
@@ -158,7 +146,7 @@ async function runSubagentSpawning(
|
||||
event = createSpawnEventWithoutThread(),
|
||||
) {
|
||||
const handlers = registerHandlersForTest(config);
|
||||
const handler = getRequiredHandler(handlers, "subagent_spawning");
|
||||
const handler = getRequiredHookHandler(handlers, "subagent_spawning");
|
||||
return await handler(event, {});
|
||||
}
|
||||
|
||||
@@ -202,7 +190,7 @@ describe("discord subagent hook handlers", () => {
|
||||
|
||||
it("binds thread routing on subagent_spawning", async () => {
|
||||
const handlers = registerHandlersForTest();
|
||||
const handler = getRequiredHandler(handlers, "subagent_spawning");
|
||||
const handler = getRequiredHookHandler(handlers, "subagent_spawning");
|
||||
|
||||
const result = await handler(createSpawnEvent(), {});
|
||||
|
||||
@@ -320,7 +308,7 @@ describe("discord subagent hook handlers", () => {
|
||||
|
||||
it("unbinds thread routing on subagent_ended", () => {
|
||||
const handlers = registerHandlersForTest();
|
||||
const handler = getRequiredHandler(handlers, "subagent_ended");
|
||||
const handler = getRequiredHookHandler(handlers, "subagent_ended");
|
||||
|
||||
handler(
|
||||
{
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/feishu";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
getRequiredHookHandler,
|
||||
registerHookHandlersForTest,
|
||||
} from "../../test-utils/subagent-hooks.js";
|
||||
import { registerFeishuSubagentHooks } from "./subagent-hooks.js";
|
||||
import {
|
||||
__testing as threadBindingTesting,
|
||||
@@ -12,26 +16,10 @@ const baseConfig = {
|
||||
};
|
||||
|
||||
function registerHandlersForTest(config: Record<string, unknown> = baseConfig) {
|
||||
const handlers = new Map<string, (event: unknown, ctx: unknown) => unknown>();
|
||||
const api = {
|
||||
return registerHookHandlersForTest<OpenClawPluginApi>({
|
||||
config,
|
||||
on: (hookName: string, handler: (event: unknown, ctx: unknown) => unknown) => {
|
||||
handlers.set(hookName, handler);
|
||||
},
|
||||
} as unknown as OpenClawPluginApi;
|
||||
registerFeishuSubagentHooks(api);
|
||||
return handlers;
|
||||
}
|
||||
|
||||
function getRequiredHandler(
|
||||
handlers: Map<string, (event: unknown, ctx: unknown) => unknown>,
|
||||
hookName: string,
|
||||
): (event: unknown, ctx: unknown) => unknown {
|
||||
const handler = handlers.get(hookName);
|
||||
if (!handler) {
|
||||
throw new Error(`expected ${hookName} hook handler`);
|
||||
}
|
||||
return handler;
|
||||
register: registerFeishuSubagentHooks,
|
||||
});
|
||||
}
|
||||
|
||||
describe("feishu subagent hook handlers", () => {
|
||||
@@ -49,7 +37,7 @@ describe("feishu subagent hook handlers", () => {
|
||||
|
||||
it("binds a Feishu DM conversation on subagent_spawning", async () => {
|
||||
const handlers = registerHandlersForTest();
|
||||
const handler = getRequiredHandler(handlers, "subagent_spawning");
|
||||
const handler = getRequiredHookHandler(handlers, "subagent_spawning");
|
||||
createFeishuThreadBindingManager({ cfg: baseConfig as any, accountId: "work" });
|
||||
|
||||
const result = await handler(
|
||||
@@ -70,7 +58,7 @@ describe("feishu subagent hook handlers", () => {
|
||||
|
||||
expect(result).toEqual({ status: "ok", threadBindingReady: true });
|
||||
|
||||
const deliveryTargetHandler = getRequiredHandler(handlers, "subagent_delivery_target");
|
||||
const deliveryTargetHandler = getRequiredHookHandler(handlers, "subagent_delivery_target");
|
||||
expect(
|
||||
deliveryTargetHandler(
|
||||
{
|
||||
@@ -96,7 +84,7 @@ describe("feishu subagent hook handlers", () => {
|
||||
|
||||
it("preserves the original Feishu DM delivery target", async () => {
|
||||
const handlers = registerHandlersForTest();
|
||||
const deliveryHandler = getRequiredHandler(handlers, "subagent_delivery_target");
|
||||
const deliveryHandler = getRequiredHookHandler(handlers, "subagent_delivery_target");
|
||||
const manager = createFeishuThreadBindingManager({ cfg: baseConfig as any, accountId: "work" });
|
||||
|
||||
manager.bindConversation({
|
||||
@@ -134,8 +122,8 @@ describe("feishu subagent hook handlers", () => {
|
||||
|
||||
it("binds a Feishu topic conversation and preserves parent context", async () => {
|
||||
const handlers = registerHandlersForTest();
|
||||
const spawnHandler = getRequiredHandler(handlers, "subagent_spawning");
|
||||
const deliveryHandler = getRequiredHandler(handlers, "subagent_delivery_target");
|
||||
const spawnHandler = getRequiredHookHandler(handlers, "subagent_spawning");
|
||||
const deliveryHandler = getRequiredHookHandler(handlers, "subagent_delivery_target");
|
||||
createFeishuThreadBindingManager({ cfg: baseConfig as any, accountId: "work" });
|
||||
|
||||
const result = await spawnHandler(
|
||||
@@ -183,8 +171,8 @@ describe("feishu subagent hook handlers", () => {
|
||||
|
||||
it("uses the requester session binding to preserve sender-scoped topic conversations", async () => {
|
||||
const handlers = registerHandlersForTest();
|
||||
const spawnHandler = getRequiredHandler(handlers, "subagent_spawning");
|
||||
const deliveryHandler = getRequiredHandler(handlers, "subagent_delivery_target");
|
||||
const spawnHandler = getRequiredHookHandler(handlers, "subagent_spawning");
|
||||
const deliveryHandler = getRequiredHookHandler(handlers, "subagent_delivery_target");
|
||||
const manager = createFeishuThreadBindingManager({ cfg: baseConfig as any, accountId: "work" });
|
||||
|
||||
manager.bindConversation({
|
||||
@@ -252,8 +240,8 @@ describe("feishu subagent hook handlers", () => {
|
||||
|
||||
it("prefers requester-matching bindings when multiple child bindings exist", async () => {
|
||||
const handlers = registerHandlersForTest();
|
||||
const spawnHandler = getRequiredHandler(handlers, "subagent_spawning");
|
||||
const deliveryHandler = getRequiredHandler(handlers, "subagent_delivery_target");
|
||||
const spawnHandler = getRequiredHookHandler(handlers, "subagent_spawning");
|
||||
const deliveryHandler = getRequiredHookHandler(handlers, "subagent_delivery_target");
|
||||
createFeishuThreadBindingManager({ cfg: baseConfig as any, accountId: "work" });
|
||||
|
||||
await spawnHandler(
|
||||
@@ -312,8 +300,8 @@ describe("feishu subagent hook handlers", () => {
|
||||
|
||||
it("fails closed when requester-session bindings remain ambiguous for the same topic", async () => {
|
||||
const handlers = registerHandlersForTest();
|
||||
const spawnHandler = getRequiredHandler(handlers, "subagent_spawning");
|
||||
const deliveryHandler = getRequiredHandler(handlers, "subagent_delivery_target");
|
||||
const spawnHandler = getRequiredHookHandler(handlers, "subagent_spawning");
|
||||
const deliveryHandler = getRequiredHookHandler(handlers, "subagent_delivery_target");
|
||||
const manager = createFeishuThreadBindingManager({ cfg: baseConfig as any, accountId: "work" });
|
||||
|
||||
manager.bindConversation({
|
||||
@@ -375,8 +363,8 @@ describe("feishu subagent hook handlers", () => {
|
||||
|
||||
it("fails closed when both topic-level and sender-scoped requester bindings exist", async () => {
|
||||
const handlers = registerHandlersForTest();
|
||||
const spawnHandler = getRequiredHandler(handlers, "subagent_spawning");
|
||||
const deliveryHandler = getRequiredHandler(handlers, "subagent_delivery_target");
|
||||
const spawnHandler = getRequiredHookHandler(handlers, "subagent_spawning");
|
||||
const deliveryHandler = getRequiredHookHandler(handlers, "subagent_delivery_target");
|
||||
const manager = createFeishuThreadBindingManager({ cfg: baseConfig as any, accountId: "work" });
|
||||
|
||||
manager.bindConversation({
|
||||
@@ -438,9 +426,9 @@ describe("feishu subagent hook handlers", () => {
|
||||
|
||||
it("no-ops for non-Feishu channels and non-threaded spawns", async () => {
|
||||
const handlers = registerHandlersForTest();
|
||||
const spawnHandler = getRequiredHandler(handlers, "subagent_spawning");
|
||||
const deliveryHandler = getRequiredHandler(handlers, "subagent_delivery_target");
|
||||
const endedHandler = getRequiredHandler(handlers, "subagent_ended");
|
||||
const spawnHandler = getRequiredHookHandler(handlers, "subagent_spawning");
|
||||
const deliveryHandler = getRequiredHookHandler(handlers, "subagent_delivery_target");
|
||||
const endedHandler = getRequiredHookHandler(handlers, "subagent_ended");
|
||||
|
||||
await expect(
|
||||
spawnHandler(
|
||||
@@ -506,7 +494,7 @@ describe("feishu subagent hook handlers", () => {
|
||||
});
|
||||
|
||||
it("returns an error for unsupported non-topic Feishu group conversations", async () => {
|
||||
const handler = getRequiredHandler(registerHandlersForTest(), "subagent_spawning");
|
||||
const handler = getRequiredHookHandler(registerHandlersForTest(), "subagent_spawning");
|
||||
createFeishuThreadBindingManager({ cfg: baseConfig as any, accountId: "work" });
|
||||
|
||||
await expect(
|
||||
@@ -532,9 +520,9 @@ describe("feishu subagent hook handlers", () => {
|
||||
|
||||
it("unbinds Feishu bindings on subagent_ended", async () => {
|
||||
const handlers = registerHandlersForTest();
|
||||
const spawnHandler = getRequiredHandler(handlers, "subagent_spawning");
|
||||
const deliveryHandler = getRequiredHandler(handlers, "subagent_delivery_target");
|
||||
const endedHandler = getRequiredHandler(handlers, "subagent_ended");
|
||||
const spawnHandler = getRequiredHookHandler(handlers, "subagent_spawning");
|
||||
const deliveryHandler = getRequiredHookHandler(handlers, "subagent_delivery_target");
|
||||
const endedHandler = getRequiredHookHandler(handlers, "subagent_ended");
|
||||
createFeishuThreadBindingManager({ cfg: baseConfig as any, accountId: "work" });
|
||||
|
||||
await spawnHandler(
|
||||
@@ -581,8 +569,8 @@ describe("feishu subagent hook handlers", () => {
|
||||
|
||||
it("fails closed when the Feishu monitor-owned binding manager is unavailable", async () => {
|
||||
const handlers = registerHandlersForTest();
|
||||
const spawnHandler = getRequiredHandler(handlers, "subagent_spawning");
|
||||
const deliveryHandler = getRequiredHandler(handlers, "subagent_delivery_target");
|
||||
const spawnHandler = getRequiredHookHandler(handlers, "subagent_spawning");
|
||||
const deliveryHandler = getRequiredHookHandler(handlers, "subagent_delivery_target");
|
||||
|
||||
await expect(
|
||||
spawnHandler(
|
||||
|
||||
25
extensions/test-utils/subagent-hooks.ts
Normal file
25
extensions/test-utils/subagent-hooks.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export function registerHookHandlersForTest<TApi>(params: {
|
||||
config: Record<string, unknown>;
|
||||
register: (api: TApi) => void;
|
||||
}) {
|
||||
const handlers = new Map<string, (event: unknown, ctx: unknown) => unknown>();
|
||||
const api = {
|
||||
config: params.config,
|
||||
on: (hookName: string, handler: (event: unknown, ctx: unknown) => unknown) => {
|
||||
handlers.set(hookName, handler);
|
||||
},
|
||||
} as TApi;
|
||||
params.register(api);
|
||||
return handlers;
|
||||
}
|
||||
|
||||
export function getRequiredHookHandler(
|
||||
handlers: Map<string, (event: unknown, ctx: unknown) => unknown>,
|
||||
hookName: string,
|
||||
): (event: unknown, ctx: unknown) => unknown {
|
||||
const handler = handlers.get(hookName);
|
||||
if (!handler) {
|
||||
throw new Error(`expected ${hookName} hook handler`);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
Reference in New Issue
Block a user