refactor(test): dedupe agent harnesses and routing fixtures

This commit is contained in:
Peter Steinberger
2026-02-18 04:48:40 +00:00
parent 8a9fddedc9
commit 31f83c86b2
12 changed files with 440 additions and 755 deletions

View File

@@ -515,191 +515,158 @@ describe("backward compatibility: peer.kind dm → direct", () => {
});
describe("role-based agent routing", () => {
test("guild+roles binding matches when member has matching role", () => {
const cfg: OpenClawConfig = {
bindings: [{ agentId: "opus", match: { channel: "discord", guildId: "g1", roles: ["r1"] } }],
type DiscordBinding = NonNullable<OpenClawConfig["bindings"]>[number];
function makeDiscordRoleBinding(
agentId: string,
params: {
roles?: string[];
peerId?: string;
includeGuildId?: boolean;
} = {},
): DiscordBinding {
return {
agentId,
match: {
channel: "discord",
...(params.includeGuildId === false ? {} : { guildId: "g1" }),
...(params.roles !== undefined ? { roles: params.roles } : {}),
...(params.peerId ? { peer: { kind: "channel", id: params.peerId } } : {}),
},
};
}
function expectDiscordRoleRoute(params: {
bindings: DiscordBinding[];
memberRoleIds?: string[];
peerId?: string;
parentPeerId?: string;
expectedAgentId: string;
expectedMatchedBy: string;
}) {
const route = resolveAgentRoute({
cfg,
cfg: { bindings: params.bindings },
channel: "discord",
guildId: "g1",
memberRoleIds: ["r1"],
peer: { kind: "channel", id: "c1" },
...(params.memberRoleIds ? { memberRoleIds: params.memberRoleIds } : {}),
peer: { kind: "channel", id: params.peerId ?? "c1" },
...(params.parentPeerId
? {
parentPeer: { kind: "channel", id: params.parentPeerId },
}
: {}),
});
expect(route.agentId).toBe(params.expectedAgentId);
expect(route.matchedBy).toBe(params.expectedMatchedBy);
}
test("guild+roles binding matches when member has matching role", () => {
expectDiscordRoleRoute({
bindings: [makeDiscordRoleBinding("opus", { roles: ["r1"] })],
memberRoleIds: ["r1"],
expectedAgentId: "opus",
expectedMatchedBy: "binding.guild+roles",
});
expect(route.agentId).toBe("opus");
expect(route.matchedBy).toBe("binding.guild+roles");
});
test("guild+roles binding skipped when no matching role", () => {
const cfg: OpenClawConfig = {
bindings: [{ agentId: "opus", match: { channel: "discord", guildId: "g1", roles: ["r1"] } }],
};
const route = resolveAgentRoute({
cfg,
channel: "discord",
guildId: "g1",
expectDiscordRoleRoute({
bindings: [makeDiscordRoleBinding("opus", { roles: ["r1"] })],
memberRoleIds: ["r2"],
peer: { kind: "channel", id: "c1" },
expectedAgentId: "main",
expectedMatchedBy: "default",
});
expect(route.agentId).toBe("main");
expect(route.matchedBy).toBe("default");
});
test("guild+roles is more specific than guild-only", () => {
const cfg: OpenClawConfig = {
expectDiscordRoleRoute({
bindings: [
{ agentId: "opus", match: { channel: "discord", guildId: "g1", roles: ["r1"] } },
{ agentId: "sonnet", match: { channel: "discord", guildId: "g1" } },
makeDiscordRoleBinding("opus", { roles: ["r1"] }),
makeDiscordRoleBinding("sonnet"),
],
};
const route = resolveAgentRoute({
cfg,
channel: "discord",
guildId: "g1",
memberRoleIds: ["r1"],
peer: { kind: "channel", id: "c1" },
expectedAgentId: "opus",
expectedMatchedBy: "binding.guild+roles",
});
expect(route.agentId).toBe("opus");
expect(route.matchedBy).toBe("binding.guild+roles");
});
test("peer binding still beats guild+roles", () => {
const cfg: OpenClawConfig = {
expectDiscordRoleRoute({
bindings: [
{
agentId: "peer-agent",
match: { channel: "discord", peer: { kind: "channel", id: "c1" } },
},
{ agentId: "roles-agent", match: { channel: "discord", guildId: "g1", roles: ["r1"] } },
makeDiscordRoleBinding("peer-agent", { peerId: "c1", includeGuildId: false }),
makeDiscordRoleBinding("roles-agent", { roles: ["r1"] }),
],
};
const route = resolveAgentRoute({
cfg,
channel: "discord",
guildId: "g1",
memberRoleIds: ["r1"],
peer: { kind: "channel", id: "c1" },
expectedAgentId: "peer-agent",
expectedMatchedBy: "binding.peer",
});
expect(route.agentId).toBe("peer-agent");
expect(route.matchedBy).toBe("binding.peer");
});
test("parent peer binding still beats guild+roles", () => {
const cfg: OpenClawConfig = {
expectDiscordRoleRoute({
bindings: [
{
agentId: "parent-agent",
match: { channel: "discord", peer: { kind: "channel", id: "parent-1" } },
},
{ agentId: "roles-agent", match: { channel: "discord", guildId: "g1", roles: ["r1"] } },
makeDiscordRoleBinding("parent-agent", {
peerId: "parent-1",
includeGuildId: false,
}),
makeDiscordRoleBinding("roles-agent", { roles: ["r1"] }),
],
};
const route = resolveAgentRoute({
cfg,
channel: "discord",
guildId: "g1",
memberRoleIds: ["r1"],
peer: { kind: "channel", id: "thread-1" },
parentPeer: { kind: "channel", id: "parent-1" },
peerId: "thread-1",
parentPeerId: "parent-1",
expectedAgentId: "parent-agent",
expectedMatchedBy: "binding.peer.parent",
});
expect(route.agentId).toBe("parent-agent");
expect(route.matchedBy).toBe("binding.peer.parent");
});
test("no memberRoleIds means guild+roles doesn't match", () => {
const cfg: OpenClawConfig = {
bindings: [{ agentId: "opus", match: { channel: "discord", guildId: "g1", roles: ["r1"] } }],
};
const route = resolveAgentRoute({
cfg,
channel: "discord",
guildId: "g1",
peer: { kind: "channel", id: "c1" },
expectDiscordRoleRoute({
bindings: [makeDiscordRoleBinding("opus", { roles: ["r1"] })],
expectedAgentId: "main",
expectedMatchedBy: "default",
});
expect(route.agentId).toBe("main");
expect(route.matchedBy).toBe("default");
});
test("first matching binding wins with multiple role bindings", () => {
const cfg: OpenClawConfig = {
expectDiscordRoleRoute({
bindings: [
{ agentId: "opus", match: { channel: "discord", guildId: "g1", roles: ["r1"] } },
{ agentId: "sonnet", match: { channel: "discord", guildId: "g1", roles: ["r2"] } },
makeDiscordRoleBinding("opus", { roles: ["r1"] }),
makeDiscordRoleBinding("sonnet", { roles: ["r2"] }),
],
};
const route = resolveAgentRoute({
cfg,
channel: "discord",
guildId: "g1",
memberRoleIds: ["r1", "r2"],
peer: { kind: "channel", id: "c1" },
expectedAgentId: "opus",
expectedMatchedBy: "binding.guild+roles",
});
expect(route.agentId).toBe("opus");
expect(route.matchedBy).toBe("binding.guild+roles");
});
test("empty roles array treated as no role restriction", () => {
const cfg: OpenClawConfig = {
bindings: [{ agentId: "opus", match: { channel: "discord", guildId: "g1", roles: [] } }],
};
const route = resolveAgentRoute({
cfg,
channel: "discord",
guildId: "g1",
expectDiscordRoleRoute({
bindings: [makeDiscordRoleBinding("opus", { roles: [] })],
memberRoleIds: ["r1"],
peer: { kind: "channel", id: "c1" },
expectedAgentId: "opus",
expectedMatchedBy: "binding.guild",
});
expect(route.agentId).toBe("opus");
expect(route.matchedBy).toBe("binding.guild");
});
test("guild+roles binding does not match as guild-only when roles do not match", () => {
const cfg: OpenClawConfig = {
bindings: [
{ agentId: "opus", match: { channel: "discord", guildId: "g1", roles: ["admin"] } },
],
};
const route = resolveAgentRoute({
cfg,
channel: "discord",
guildId: "g1",
expectDiscordRoleRoute({
bindings: [makeDiscordRoleBinding("opus", { roles: ["admin"] })],
memberRoleIds: ["regular"],
peer: { kind: "channel", id: "c1" },
expectedAgentId: "main",
expectedMatchedBy: "default",
});
expect(route.agentId).toBe("main");
expect(route.matchedBy).toBe("default");
});
test("peer+guild+roles binding does not act as guild+roles fallback when peer mismatches", () => {
const cfg: OpenClawConfig = {
expectDiscordRoleRoute({
bindings: [
{
agentId: "peer-roles",
match: {
channel: "discord",
peer: { kind: "channel", id: "c-target" },
guildId: "g1",
roles: ["r1"],
},
},
{
agentId: "guild-roles",
match: {
channel: "discord",
guildId: "g1",
roles: ["r1"],
},
},
makeDiscordRoleBinding("peer-roles", { peerId: "c-target", roles: ["r1"] }),
makeDiscordRoleBinding("guild-roles", { roles: ["r1"] }),
],
};
const route = resolveAgentRoute({
cfg,
channel: "discord",
guildId: "g1",
memberRoleIds: ["r1"],
peer: { kind: "channel", id: "c-other" },
peerId: "c-other",
expectedAgentId: "guild-roles",
expectedMatchedBy: "binding.guild+roles",
});
expect(route.agentId).toBe("guild-roles");
expect(route.matchedBy).toBe("binding.guild+roles");
});
});