mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:50:43 +00:00
115 lines
3.3 KiB
TypeScript
115 lines
3.3 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import {
|
|
__testing,
|
|
registerSessionBindingAdapter,
|
|
type SessionBindingAdapter,
|
|
type SessionBindingRecord,
|
|
} from "../../infra/outbound/session-binding-service.js";
|
|
import type { ResolvedAgentRoute } from "../../routing/resolve-route.js";
|
|
import { resolveRuntimeConversationBindingRoute } from "./binding-routing.js";
|
|
|
|
function createRoute(): ResolvedAgentRoute {
|
|
return {
|
|
agentId: "main",
|
|
channel: "demo",
|
|
accountId: "default",
|
|
sessionKey: "agent:main:main",
|
|
mainSessionKey: "agent:main:main",
|
|
lastRoutePolicy: "main",
|
|
matchedBy: "default",
|
|
};
|
|
}
|
|
|
|
function createBinding(overrides?: Partial<SessionBindingRecord>): SessionBindingRecord {
|
|
return {
|
|
bindingId: "binding-1",
|
|
targetSessionKey: "agent:review:acp:session-1",
|
|
targetKind: "session",
|
|
conversation: {
|
|
channel: "demo",
|
|
accountId: "default",
|
|
conversationId: "room-1",
|
|
},
|
|
status: "active",
|
|
boundAt: 1,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function registerAdapter(record: SessionBindingRecord | null): {
|
|
resolveByConversation: ReturnType<typeof vi.fn>;
|
|
touch: ReturnType<typeof vi.fn>;
|
|
} {
|
|
const resolveByConversation = vi.fn<SessionBindingAdapter["resolveByConversation"]>(() => record);
|
|
const touch = vi.fn<NonNullable<SessionBindingAdapter["touch"]>>();
|
|
registerSessionBindingAdapter({
|
|
channel: "demo",
|
|
accountId: "default",
|
|
listBySession: () => [],
|
|
resolveByConversation,
|
|
touch,
|
|
});
|
|
return { resolveByConversation, touch };
|
|
}
|
|
|
|
describe("runtime conversation binding route", () => {
|
|
beforeEach(() => {
|
|
__testing.resetSessionBindingAdaptersForTests();
|
|
});
|
|
|
|
it("rewrites the route to a runtime-bound ACP session and touches the binding", () => {
|
|
const binding = createBinding();
|
|
const { resolveByConversation, touch } = registerAdapter(binding);
|
|
|
|
const result = resolveRuntimeConversationBindingRoute({
|
|
route: createRoute(),
|
|
conversation: {
|
|
channel: "demo",
|
|
accountId: "default",
|
|
conversationId: "room-1",
|
|
},
|
|
});
|
|
|
|
expect(resolveByConversation).toHaveBeenCalledWith({
|
|
channel: "demo",
|
|
accountId: "default",
|
|
conversationId: "room-1",
|
|
});
|
|
expect(touch).toHaveBeenCalledWith("binding-1", undefined);
|
|
expect(result.boundSessionKey).toBe("agent:review:acp:session-1");
|
|
expect(result.boundAgentId).toBe("review");
|
|
expect(result.route).toMatchObject({
|
|
agentId: "review",
|
|
sessionKey: "agent:review:acp:session-1",
|
|
lastRoutePolicy: "session",
|
|
matchedBy: "binding.channel",
|
|
});
|
|
});
|
|
|
|
it("touches plugin-owned bindings without rewriting the channel route", () => {
|
|
const route = createRoute();
|
|
const binding = createBinding({
|
|
metadata: {
|
|
pluginBindingOwner: "plugin",
|
|
pluginId: "demo-plugin",
|
|
pluginRoot: "/tmp/demo-plugin",
|
|
},
|
|
});
|
|
const { touch } = registerAdapter(binding);
|
|
|
|
const result = resolveRuntimeConversationBindingRoute({
|
|
route,
|
|
conversation: {
|
|
channel: "demo",
|
|
accountId: "default",
|
|
conversationId: "room-1",
|
|
},
|
|
});
|
|
|
|
expect(touch).toHaveBeenCalledWith("binding-1", undefined);
|
|
expect(result.bindingRecord).toBe(binding);
|
|
expect(result.boundSessionKey).toBeUndefined();
|
|
expect(result.route).toBe(route);
|
|
});
|
|
});
|