Adds optional path_mappings to agent.yaml so host directories are transparently mapped to virtual paths in agent responses and CLAUDE.md. Sanitizer replaces host paths (longest first, both slash styles) before repoRoot/home substitutions. /new-agent accepts a host:virtual path option with Windows-safe splitting and fs.existsSync validation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
125 lines
3.6 KiB
TypeScript
125 lines
3.6 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
|
|
/**
|
|
* Integration test: verifies that routeMessage serializes same-channel messages
|
|
* through ChannelQueue so they are processed in FIFO order.
|
|
*
|
|
* runAgent is mocked to record call order without spawning a real process.
|
|
* discord.js and the database are also mocked so no real I/O occurs.
|
|
*/
|
|
|
|
// ---- Mock runAgent BEFORE importing router ----
|
|
const runOrder: string[] = [];
|
|
|
|
vi.mock("../../src/agent/runner", () => ({
|
|
runAgent: vi.fn(async ({ userMessage }: { userMessage: string }) => {
|
|
// Simulate brief async work so the promise chain is exercised
|
|
await new Promise<void>((resolve) => setTimeout(resolve, 5));
|
|
runOrder.push(userMessage);
|
|
return `echo: ${userMessage}`;
|
|
}),
|
|
}));
|
|
|
|
// ---- Mock loadAgentIdentity so no real agent.yaml is needed ----
|
|
vi.mock("../../src/agent/identity", () => ({
|
|
loadAgentIdentity: vi.fn(() => ({
|
|
name: "test-agent",
|
|
channel_id: "ch-999",
|
|
model: "claude-opus-4-5",
|
|
path_mappings: [],
|
|
})),
|
|
}));
|
|
|
|
// ---- Import after mock registration ----
|
|
import { routeMessage } from "../../src/router";
|
|
|
|
// ---- Helpers ----
|
|
|
|
/** Builds a minimal fake discord.js Message object. */
|
|
function makeMessage(channelId: string, content: string) {
|
|
const sent: string[] = [];
|
|
const channel = {
|
|
name: "test-channel",
|
|
sendTyping: vi.fn().mockResolvedValue(undefined),
|
|
send: vi.fn(async (text: string) => {
|
|
sent.push(text);
|
|
return { id: `msg-${Math.random()}` };
|
|
}),
|
|
};
|
|
|
|
return {
|
|
channelId,
|
|
content,
|
|
id: `mid-${content}`,
|
|
author: { username: "tester" },
|
|
channel,
|
|
_sent: sent,
|
|
} as unknown as import("discord.js").Message & { _sent: string[] };
|
|
}
|
|
|
|
/** Minimal fake DisclawDatabase. */
|
|
function makeDb(channelId: string) {
|
|
return {
|
|
getWorkspaceByChannelId: vi.fn((_id: string) => ({
|
|
id: 1,
|
|
workspace_path: "/fake/workspace",
|
|
})),
|
|
getConversationHistory: vi.fn(() => []),
|
|
addConversation: vi.fn(),
|
|
} as unknown as import("../../src/db/database").DisclawDatabase;
|
|
}
|
|
|
|
/** Minimal fake DisclawConfig. */
|
|
const fakeConfig = {} as import("../../src/config/loader").DisclawConfig;
|
|
|
|
// ---- Tests ----
|
|
|
|
describe("channel-queue / router integration", () => {
|
|
beforeEach(() => {
|
|
runOrder.length = 0;
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it("processes 3 messages in the same channel in FIFO order", async () => {
|
|
const channelId = "ch-999";
|
|
const db = makeDb(channelId);
|
|
|
|
// Fire all three without awaiting — they should serialise internally
|
|
const p1 = routeMessage(makeMessage(channelId, "first"), db, fakeConfig, null);
|
|
const p2 = routeMessage(makeMessage(channelId, "second"), db, fakeConfig, null);
|
|
const p3 = routeMessage(makeMessage(channelId, "third"), db, fakeConfig, null);
|
|
|
|
await Promise.all([p1, p2, p3]);
|
|
|
|
expect(runOrder).toEqual(["first", "second", "third"]);
|
|
});
|
|
|
|
it("returns true for every handled message", async () => {
|
|
const channelId = "ch-handled";
|
|
const db = makeDb(channelId);
|
|
|
|
const results = await Promise.all([
|
|
routeMessage(makeMessage(channelId, "a"), db, fakeConfig, null),
|
|
routeMessage(makeMessage(channelId, "b"), db, fakeConfig, null),
|
|
]);
|
|
|
|
expect(results).toEqual([true, true]);
|
|
});
|
|
|
|
it("returns false when channel is the management channel", async () => {
|
|
const channelId = "ch-mgmt";
|
|
const db = makeDb(channelId);
|
|
|
|
const result = await routeMessage(
|
|
makeMessage(channelId, "hi"),
|
|
db,
|
|
fakeConfig,
|
|
channelId // same id => management channel
|
|
);
|
|
|
|
expect(result).toBe(false);
|
|
});
|
|
});
|