From 9eda40234f467df08ed812632fe556c77718f5bc Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 12 Dec 2025 16:35:47 +0000 Subject: [PATCH] test: cover main last-channel routing --- src/config/sessions.test.ts | 44 +++++++++++- src/gateway/server.test.ts | 138 ++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+), 1 deletion(-) diff --git a/src/config/sessions.test.ts b/src/config/sessions.test.ts index 41921f2a8..33c9a5d10 100644 --- a/src/config/sessions.test.ts +++ b/src/config/sessions.test.ts @@ -1,6 +1,14 @@ +import os from "node:os"; +import path from "node:path"; +import fs from "node:fs/promises"; import { describe, expect, it } from "vitest"; -import { deriveSessionKey, resolveSessionKey } from "./sessions.js"; +import { + deriveSessionKey, + loadSessionStore, + resolveSessionKey, + updateLastRoute, +} from "./sessions.js"; describe("sessions", () => { it("returns normalized per-sender key", () => { @@ -52,4 +60,38 @@ describe("sessions", () => { resolveSessionKey("per-sender", { From: "12345-678@g.us" }, "main"), ).toBe("group:12345-678@g.us"); }); + + it("updateLastRoute persists channel and target", async () => { + const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-sessions-")); + const storePath = path.join(dir, "sessions.json"); + await fs.writeFile( + storePath, + JSON.stringify( + { + main: { + sessionId: "sess-1", + updatedAt: 123, + systemSent: true, + thinkingLevel: "low", + }, + }, + null, + 2, + ), + "utf-8", + ); + + await updateLastRoute({ + storePath, + sessionKey: "main", + channel: "telegram", + to: " 12345 ", + }); + + const store = loadSessionStore(storePath); + expect(store.main?.sessionId).toBe("sess-1"); + expect(store.main?.updatedAt).toBeGreaterThanOrEqual(123); + expect(store.main?.lastChannel).toBe("telegram"); + expect(store.main?.lastTo).toBe("12345"); + }); }); diff --git a/src/gateway/server.test.ts b/src/gateway/server.test.ts index 9407a67fe..0aa2bd101 100644 --- a/src/gateway/server.test.ts +++ b/src/gateway/server.test.ts @@ -1,10 +1,27 @@ +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; import { type AddressInfo, createServer } from "node:net"; import { describe, expect, test, vi } from "vitest"; import { WebSocket } from "ws"; +import { agentCommand } from "../commands/agent.js"; import { emitAgentEvent } from "../infra/agent-events.js"; import { GatewayLockError } from "../infra/gateway-lock.js"; import { startGatewayServer } from "./server.js"; +let testSessionStorePath: string | undefined; +vi.mock("../config/config.js", () => ({ + loadConfig: () => ({ + inbound: { + reply: { + mode: "command", + command: ["echo", "ok"], + session: { mainKey: "main", store: testSessionStorePath }, + }, + }, + }), +})); + vi.mock("../commands/health.js", () => ({ getHealthSnapshot: vi.fn().mockResolvedValue({ ok: true, stub: true }), })); @@ -90,6 +107,127 @@ async function startServerWithClient(token?: string) { } describe("gateway server", () => { + test("agent routes main last-channel telegram", async () => { + const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-gw-")); + testSessionStorePath = path.join(dir, "sessions.json"); + await fs.writeFile( + testSessionStorePath, + JSON.stringify( + { + main: { + sessionId: "sess-main", + updatedAt: Date.now(), + lastChannel: "telegram", + lastTo: "123", + }, + }, + null, + 2, + ), + "utf-8", + ); + + const { server, ws } = await startServerWithClient(); + ws.send( + JSON.stringify({ + type: "hello", + minProtocol: 1, + maxProtocol: 1, + client: { name: "test", version: "1", platform: "test", mode: "test" }, + caps: [], + }), + ); + await onceMessage(ws, (o) => o.type === "hello-ok"); + + ws.send( + JSON.stringify({ + type: "req", + id: "agent-last", + method: "agent", + params: { + message: "hi", + sessionKey: "main", + channel: "last", + deliver: true, + idempotencyKey: "idem-agent-last", + }, + }), + ); + await onceMessage(ws, (o) => o.type === "res" && o.id === "agent-last"); + + const spy = vi.mocked(agentCommand); + expect(spy).toHaveBeenCalled(); + const call = spy.mock.calls.at(-1)?.[0] as Record; + expect(call.provider).toBe("telegram"); + expect(call.to).toBe("123"); + expect(call.deliver).toBe(true); + expect(call.bestEffortDeliver).toBe(true); + expect(call.sessionId).toBe("sess-main"); + + ws.close(); + await server.close(); + }); + + test("agent forces no-deliver when last-channel is webchat", async () => { + const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-gw-")); + testSessionStorePath = path.join(dir, "sessions.json"); + await fs.writeFile( + testSessionStorePath, + JSON.stringify( + { + main: { + sessionId: "sess-main-webchat", + updatedAt: Date.now(), + lastChannel: "webchat", + lastTo: "ignored", + }, + }, + null, + 2, + ), + "utf-8", + ); + + const { server, ws } = await startServerWithClient(); + ws.send( + JSON.stringify({ + type: "hello", + minProtocol: 1, + maxProtocol: 1, + client: { name: "test", version: "1", platform: "test", mode: "test" }, + caps: [], + }), + ); + await onceMessage(ws, (o) => o.type === "hello-ok"); + + ws.send( + JSON.stringify({ + type: "req", + id: "agent-webchat", + method: "agent", + params: { + message: "hi", + sessionKey: "main", + channel: "last", + deliver: true, + idempotencyKey: "idem-agent-webchat", + }, + }), + ); + await onceMessage(ws, (o) => o.type === "res" && o.id === "agent-webchat"); + + const spy = vi.mocked(agentCommand); + expect(spy).toHaveBeenCalled(); + const call = spy.mock.calls.at(-1)?.[0] as Record; + expect(call.provider).toBe("webchat"); + expect(call.deliver).toBe(false); + expect(call.bestEffortDeliver).toBe(true); + expect(call.sessionId).toBe("sess-main-webchat"); + + ws.close(); + await server.close(); + }); + test("rejects protocol mismatch", async () => { const { server, ws } = await startServerWithClient(); ws.send(