Download src/channels/plugins/binding-routing.test.ts from SaylorTwift/openclaw: direct link, hf CLI and curl.
- Browser
- Download file 7.82 kB
-
https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/channels/plugins/binding-routing.test.ts
- Command line
-
hf download hf://SaylorTwift/openclaw/src/channels/plugins/binding-routing.test.ts
-
curl -L -o binding-routing.test.ts https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/channels/plugins/binding-routing.test.ts
7.82 kB
| // Binding routing tests cover channel binding selection and message routing behavior. | |
| import { afterEach, 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 { | |
| ensureConfiguredBindingRouteReady, | |
| resolveRuntimeConversationBindingRoute, | |
| type RuntimeConversationBindingRouteResult, | |
| } from "./binding-routing.js"; | |
| import { registerStatefulBindingTargetDriver } from "./stateful-target-drivers.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: record?.conversation.channel ?? "demo", | |
| accountId: record?.conversation.accountId ?? "default", | |
| listBySession: () => [], | |
| resolveByConversation, | |
| touch, | |
| }); | |
| return { resolveByConversation, touch }; | |
| } | |
| describe("runtime conversation binding route", () => { | |
| beforeEach(() => { | |
| testing.resetSessionBindingAdaptersForTests(); | |
| }); | |
| it("keeps the stable runtime-route result structurally assignable", () => { | |
| const result: RuntimeConversationBindingRouteResult = { | |
| bindingRecord: null, | |
| route: createRoute(), | |
| }; | |
| expect(result.bindingOwnerAvailable).toBeUndefined(); | |
| }); | |
| it("rewrites the route and touches only the owning channel account's binding", () => { | |
| const binding = createBinding(); | |
| const { resolveByConversation, touch } = registerAdapter(binding); | |
| const siblingTouches = [ | |
| { channel: "other", accountId: "default" }, | |
| { channel: "demo", accountId: "other" }, | |
| ].map( | |
| (scope) => | |
| registerAdapter(createBinding({ conversation: { ...binding.conversation, ...scope } })) | |
| .touch, | |
| ); | |
| 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); | |
| for (const siblingTouch of siblingTouches) { | |
| expect(siblingTouch).not.toHaveBeenCalled(); | |
| } | |
| expect(result.boundSessionKey).toBe("agent:review:acp:session-1"); | |
| expect(result.boundAgentId).toBe("review"); | |
| expect(result.route).toEqual({ | |
| agentId: "review", | |
| accountId: "default", | |
| channel: "demo", | |
| sessionKey: "agent:review:acp:session-1", | |
| mainSessionKey: "agent:main:main", | |
| 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); | |
| }); | |
| it.each([ | |
| { targetSessionKey: "global", metadata: { agentId: "review" }, agentId: "review" }, | |
| { targetSessionKey: "global", metadata: undefined, agentId: "main" }, | |
| { | |
| targetSessionKey: "agent:review:session-1", | |
| metadata: { agentId: "other" }, | |
| agentId: "review", | |
| }, | |
| ])("resolves $targetSessionKey to owner $agentId", ({ targetSessionKey, metadata, agentId }) => { | |
| const binding = createBinding({ targetSessionKey, metadata }); | |
| registerAdapter(binding); | |
| const result = resolveRuntimeConversationBindingRoute({ | |
| route: createRoute(), | |
| conversation: binding.conversation, | |
| }); | |
| expect(result.route).toMatchObject({ sessionKey: targetSessionKey, agentId }); | |
| expect(result.boundAgentId).toBe(agentId); | |
| }); | |
| it("rejects an opaque target when its plugin ownership metadata is missing", () => { | |
| const binding = createBinding({ | |
| targetSessionKey: "plugin-thread-1", | |
| metadata: { agentId: "review" }, | |
| }); | |
| registerAdapter(binding); | |
| expect(() => | |
| resolveRuntimeConversationBindingRoute({ | |
| route: createRoute(), | |
| conversation: binding.conversation, | |
| }), | |
| ).toThrow(); | |
| }); | |
| it("inspects a runtime-bound route without touching the binding", () => { | |
| const { touch } = registerAdapter(createBinding()); | |
| const result = resolveRuntimeConversationBindingRoute({ | |
| route: createRoute(), | |
| touchBinding: false, | |
| conversation: { | |
| channel: "demo", | |
| accountId: "default", | |
| conversationId: "room-1", | |
| }, | |
| }); | |
| expect(touch).not.toHaveBeenCalled(); | |
| expect(result.bindingOwnerAvailable).toBe(true); | |
| expect(result.boundSessionKey).toBe("agent:review:acp:session-1"); | |
| }); | |
| it("ignores runtime bindings that target isolated cron run sessions", () => { | |
| const route = createRoute(); | |
| const binding = createBinding({ | |
| targetSessionKey: "agent:youtube:cron:monthly-report:run:closed-run-1", | |
| }); | |
| const { touch } = registerAdapter(binding); | |
| const result = resolveRuntimeConversationBindingRoute({ | |
| route, | |
| conversation: { | |
| channel: "demo", | |
| accountId: "default", | |
| conversationId: "room-1", | |
| }, | |
| }); | |
| expect(touch).not.toHaveBeenCalled(); | |
| expect(result.bindingRecord).toBeNull(); | |
| expect(result.boundSessionKey).toBeUndefined(); | |
| expect(result.route).toBe(route); | |
| }); | |
| }); | |
| describe("ensureConfiguredBindingRouteReady", () => { | |
| let unregisterDriver: (() => void) | undefined; | |
| afterEach(() => { | |
| vi.useRealTimers(); | |
| unregisterDriver?.(); | |
| }); | |
| it("returns a bounded failure when target readiness never settles", async () => { | |
| vi.useFakeTimers(); | |
| unregisterDriver = registerStatefulBindingTargetDriver({ | |
| id: "slow", | |
| ensureReady: async () => await new Promise<never>(() => {}), | |
| ensureSession: async () => ({ | |
| ok: false, | |
| sessionKey: "agent:slow:binding", | |
| error: "not used", | |
| }), | |
| }); | |
| const resultPromise = ensureConfiguredBindingRouteReady({ | |
| cfg: {} as never, | |
| bindingResolution: { statefulTarget: { driverId: "slow" } } as never, | |
| }); | |
| await vi.advanceTimersByTimeAsync(30_000); | |
| await expect(resultPromise).resolves.toEqual({ | |
| ok: false, | |
| error: "Configured binding route ready check timed out", | |
| }); | |
| }); | |
| }); | |