openclaw/src/channels/plugins/binding-targets.test.ts
Bob ea15819ecf
ACP: harden startup and move configured routing behind plugin seams (#48197)
* ACPX: keep plugin-local runtime installs out of dist

* Gateway: harden ACP startup and service PATH

* ACP: reinitialize error-state configured bindings

* ACP: classify pre-turn runtime failures as session init failures

* Plugins: move configured ACP routing behind channel seams

* Telegram tests: align startup probe assertions after rebase

* Discord: harden ACP configured binding recovery

* ACP: recover Discord bindings after stale runtime exits

* ACPX: replace dead sessions during ensure

* Discord: harden ACP binding recovery

* Discord: fix review follow-ups

* ACP bindings: load channel snapshots across workspaces

* ACP bindings: cache snapshot channel plugin resolution

* Experiments: add ACP pluginification holy grail plan

* Experiments: rename ACP pluginification plan doc

* Experiments: drop old ACP pluginification doc path

* ACP: move configured bindings behind plugin services

* Experiments: update bindings capability architecture plan

* Bindings: isolate configured binding routing and targets

* Discord tests: fix runtime env helper path

* Tests: fix channel binding CI regressions

* Tests: normalize ACP workspace assertion on Windows

* Bindings: isolate configured binding registry

* Bindings: finish configured binding cleanup

* Bindings: finish generic cleanup

* Bindings: align runtime approval callbacks

* ACP: delete residual bindings barrel

* Bindings: restore legacy compatibility

* Revert "Bindings: restore legacy compatibility"

This reverts commit ac2ed68fa2426ecc874d68278c71c71ad363fcfe.

* Tests: drop ACP route legacy helper names

* Discord/ACP: fix binding regressions

---------

Co-authored-by: Onur <2453968+osolmaz@users.noreply.github.com>
2026-03-17 17:27:52 +01:00

210 lines
5.6 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "vitest";
import {
ensureConfiguredBindingTargetReady,
ensureConfiguredBindingTargetSession,
resetConfiguredBindingTargetInPlace,
} from "./binding-targets.js";
import type { ConfiguredBindingResolution } from "./binding-types.js";
import {
registerStatefulBindingTargetDriver,
unregisterStatefulBindingTargetDriver,
type StatefulBindingTargetDriver,
} from "./stateful-target-drivers.js";
function createBindingResolution(driverId: string): ConfiguredBindingResolution {
return {
conversation: {
channel: "discord",
accountId: "default",
conversationId: "123",
},
compiledBinding: {
channel: "discord",
binding: {
type: "acp" as const,
agentId: "codex",
match: {
channel: "discord",
peer: {
kind: "channel" as const,
id: "123",
},
},
acp: {
mode: "persistent",
},
},
bindingConversationId: "123",
target: {
conversationId: "123",
},
agentId: "codex",
provider: {
compileConfiguredBinding: () => ({
conversationId: "123",
}),
matchInboundConversation: () => ({
conversationId: "123",
}),
},
targetFactory: {
driverId,
materialize: () => ({
record: {
bindingId: "binding:123",
targetSessionKey: `agent:codex:${driverId}`,
targetKind: "session",
conversation: {
channel: "discord",
accountId: "default",
conversationId: "123",
},
status: "active",
boundAt: 0,
},
statefulTarget: {
kind: "stateful",
driverId,
sessionKey: `agent:codex:${driverId}`,
agentId: "codex",
},
}),
},
},
match: {
conversationId: "123",
},
record: {
bindingId: "binding:123",
targetSessionKey: `agent:codex:${driverId}`,
targetKind: "session",
conversation: {
channel: "discord",
accountId: "default",
conversationId: "123",
},
status: "active",
boundAt: 0,
},
statefulTarget: {
kind: "stateful",
driverId,
sessionKey: `agent:codex:${driverId}`,
agentId: "codex",
},
};
}
afterEach(() => {
unregisterStatefulBindingTargetDriver("test-driver");
});
describe("binding target drivers", () => {
it("delegates ensureReady and ensureSession to the resolved driver", async () => {
const ensureReady = vi.fn(async () => ({ ok: true as const }));
const ensureSession = vi.fn(async () => ({
ok: true as const,
sessionKey: "agent:codex:test-driver",
}));
const driver: StatefulBindingTargetDriver = {
id: "test-driver",
ensureReady,
ensureSession,
};
registerStatefulBindingTargetDriver(driver);
const bindingResolution = createBindingResolution("test-driver");
await expect(
ensureConfiguredBindingTargetReady({
cfg: {} as never,
bindingResolution,
}),
).resolves.toEqual({ ok: true });
await expect(
ensureConfiguredBindingTargetSession({
cfg: {} as never,
bindingResolution,
}),
).resolves.toEqual({
ok: true,
sessionKey: "agent:codex:test-driver",
});
expect(ensureReady).toHaveBeenCalledTimes(1);
expect(ensureReady).toHaveBeenCalledWith({
cfg: {} as never,
bindingResolution,
});
expect(ensureSession).toHaveBeenCalledTimes(1);
expect(ensureSession).toHaveBeenCalledWith({
cfg: {} as never,
bindingResolution,
});
});
it("resolves resetInPlace through the driver session-key lookup", async () => {
const resetInPlace = vi.fn(async () => ({ ok: true as const }));
const driver: StatefulBindingTargetDriver = {
id: "test-driver",
ensureReady: async () => ({ ok: true }),
ensureSession: async () => ({
ok: true,
sessionKey: "agent:codex:test-driver",
}),
resolveTargetBySessionKey: ({ sessionKey }) => ({
kind: "stateful",
driverId: "test-driver",
sessionKey,
agentId: "codex",
}),
resetInPlace,
};
registerStatefulBindingTargetDriver(driver);
await expect(
resetConfiguredBindingTargetInPlace({
cfg: {} as never,
sessionKey: "agent:codex:test-driver",
reason: "reset",
}),
).resolves.toEqual({ ok: true });
expect(resetInPlace).toHaveBeenCalledTimes(1);
expect(resetInPlace).toHaveBeenCalledWith({
cfg: {} as never,
sessionKey: "agent:codex:test-driver",
reason: "reset",
bindingTarget: {
kind: "stateful",
driverId: "test-driver",
sessionKey: "agent:codex:test-driver",
agentId: "codex",
},
});
});
it("returns a typed error when no driver is registered", async () => {
const bindingResolution = createBindingResolution("missing-driver");
await expect(
ensureConfiguredBindingTargetReady({
cfg: {} as never,
bindingResolution,
}),
).resolves.toEqual({
ok: false,
error: "Configured binding target driver unavailable: missing-driver",
});
await expect(
ensureConfiguredBindingTargetSession({
cfg: {} as never,
bindingResolution,
}),
).resolves.toEqual({
ok: false,
sessionKey: "agent:codex:missing-driver",
error: "Configured binding target driver unavailable: missing-driver",
});
});
});