From f62291a0eb186e948a320530a7b2467a94463e7b Mon Sep 17 00:00:00 2001 From: Matthijs Hoekstra Date: Thu, 29 Jan 2026 13:43:04 +0100 Subject: [PATCH] test: add comprehensive tests for OpenRouter routing configuration and validation --- .../models-config.openrouter-routing.test.ts | 117 ++++++++++++++++++ src/config/openrouter-routing-example.test.ts | 42 +++++++ .../zod-schema.openrouter-routing.test.ts | 98 +++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 src/agents/models-config.openrouter-routing.test.ts create mode 100644 src/config/openrouter-routing-example.test.ts create mode 100644 src/config/zod-schema.openrouter-routing.test.ts diff --git a/src/agents/models-config.openrouter-routing.test.ts b/src/agents/models-config.openrouter-routing.test.ts new file mode 100644 index 000000000..bdee42e14 --- /dev/null +++ b/src/agents/models-config.openrouter-routing.test.ts @@ -0,0 +1,117 @@ +import { describe, expect, it } from "vitest"; +import type { MoltbotConfig } from "../config/config.js"; + +describe("OpenRouter routing", () => { + it("config accepts openRouterRouting with 'only' field", () => { + const cfg: MoltbotConfig = { + env: { + OPENROUTER_API_KEY: "sk-or-test-key", + }, + agents: { + defaults: { + model: { primary: "openrouter/anthropic/claude-sonnet-4-5" }, + models: { + "openrouter/anthropic/claude-sonnet-4-5": { + alias: "Claude Sonnet", + compat: { + openRouterRouting: { + only: ["anthropic"], + }, + }, + }, + }, + }, + }, + }; + + const modelKey = "openrouter/anthropic/claude-sonnet-4-5"; + const modelConfig = cfg.agents?.defaults?.models?.[modelKey]; + + expect(modelConfig?.compat?.openRouterRouting).toEqual({ + only: ["anthropic"], + }); + }); + + it("config accepts openRouterRouting with 'order' field", () => { + const cfg: MoltbotConfig = { + env: { + OPENROUTER_API_KEY: "sk-or-test-key", + }, + agents: { + defaults: { + model: { primary: "openrouter/openai/gpt-5.2" }, + models: { + "openrouter/openai/gpt-5.2": { + alias: "GPT-5.2", + compat: { + openRouterRouting: { + order: ["anthropic", "openai"], + }, + }, + }, + }, + }, + }, + }; + + const modelKey = "openrouter/openai/gpt-5.2"; + const modelConfig = cfg.agents?.defaults?.models?.[modelKey]; + + expect(modelConfig?.compat?.openRouterRouting).toEqual({ + order: ["anthropic", "openai"], + }); + }); + + it("validates openRouterRouting config shape", () => { + const testCases = [ + { + name: "only with single provider", + config: { only: ["anthropic"] }, + }, + { + name: "only with multiple providers", + config: { only: ["anthropic", "openai"] }, + }, + { + name: "order with single provider", + config: { order: ["anthropic"] }, + }, + { + name: "order with multiple providers", + config: { order: ["anthropic", "openai"] }, + }, + { + name: "empty routing", + config: {}, + }, + { + name: "both only and order", + config: { only: ["anthropic"], order: ["openai"] }, + }, + ]; + + for (const testCase of testCases) { + const cfg: MoltbotConfig = { + env: { OPENROUTER_API_KEY: "sk-or-test-key" }, + agents: { + defaults: { + model: { primary: "openrouter/anthropic/claude-sonnet-4-5" }, + models: { + "openrouter/anthropic/claude-sonnet-4-5": { + compat: { + openRouterRouting: testCase.config, + }, + }, + }, + }, + }, + }; + + expect(() => { + const modelConfig = + cfg.agents?.defaults?.models?.["openrouter/anthropic/claude-sonnet-4-5"]; + expect(modelConfig?.compat?.openRouterRouting).toEqual(testCase.config); + }).not.toThrow(); + } + }); +}); diff --git a/src/config/openrouter-routing-example.test.ts b/src/config/openrouter-routing-example.test.ts new file mode 100644 index 000000000..72e888999 --- /dev/null +++ b/src/config/openrouter-routing-example.test.ts @@ -0,0 +1,42 @@ +import { expect, it } from "vitest"; + +it("example: openRouterRouting configuration", () => { + const config = { + agents: { + defaults: { + model: { primary: "openrouter/anthropic/claude-sonnet-4-5" }, + models: { + "openrouter/anthropic/claude-sonnet-4-5": { + alias: "Claude Sonnet", + compat: { + openRouterRouting: { + only: ["anthropic"], + }, + }, + }, + "openrouter/openai/gpt-5.2": { + alias: "GPT-5.2", + compat: { + openRouterRouting: { + order: ["anthropic", "openai"], + }, + }, + }, + }, + }, + }, + }; + + expect( + config.agents.defaults.models?.["openrouter/anthropic/claude-sonnet-4-5"]?.compat + ?.openRouterRouting, + ).toEqual({ + only: ["anthropic"], + }); + + expect( + config.agents.defaults.models?.["openrouter/openai/gpt-5.2"]?.compat?.openRouterRouting, + ).toEqual({ + order: ["anthropic", "openai"], + }); +}); diff --git a/src/config/zod-schema.openrouter-routing.test.ts b/src/config/zod-schema.openrouter-routing.test.ts new file mode 100644 index 000000000..d56b606bd --- /dev/null +++ b/src/config/zod-schema.openrouter-routing.test.ts @@ -0,0 +1,98 @@ +import fs from "node:fs/promises"; +import path from "node:path"; +import { describe, expect, it } from "vitest"; +import { withTempHome } from "./test-helpers.js"; + +describe("OpenRouter routing integration", () => { + it("validates and loads openRouterRouting config from moltbot.json", async () => { + await withTempHome(async (home) => { + const configDir = path.join(home, ".clawdbot"); + await fs.mkdir(configDir, { recursive: true }); + await fs.writeFile( + path.join(configDir, "moltbot.json"), + JSON.stringify( + { + agents: { + defaults: { + model: { primary: "openrouter/anthropic/claude-sonnet-4-5" }, + models: { + "openrouter/anthropic/claude-sonnet-4-5": { + alias: "Claude Sonnet", + compat: { + openRouterRouting: { + only: ["anthropic"], + }, + }, + }, + "openrouter/openai/gpt-5.2": { + alias: "GPT-5.2", + compat: { + openRouterRouting: { + order: ["anthropic", "openai"], + }, + }, + }, + }, + }, + }, + }, + null, + 2, + ), + "utf-8", + ); + + const { loadConfig } = await import("./config.js"); + const cfg = loadConfig(); + + expect(cfg.agents?.defaults?.model?.primary).toBe("openrouter/anthropic/claude-sonnet-4-5"); + + const sonnetModel = cfg.agents?.defaults?.models?.["openrouter/anthropic/claude-sonnet-4-5"]; + expect(sonnetModel?.alias).toBe("Claude Sonnet"); + expect(sonnetModel?.compat?.openRouterRouting).toEqual({ + only: ["anthropic"], + }); + + const gptModel = cfg.agents?.defaults?.models?.["openrouter/openai/gpt-5.2"]; + expect(gptModel?.alias).toBe("GPT-5.2"); + expect(gptModel?.compat?.openRouterRouting).toEqual({ + order: ["anthropic", "openai"], + }); + }); + }); + + it("accepts openRouterRouting with empty config", async () => { + await withTempHome(async (home) => { + const configDir = path.join(home, ".clawdbot"); + await fs.mkdir(configDir, { recursive: true }); + await fs.writeFile( + path.join(configDir, "moltbot.json"), + JSON.stringify( + { + agents: { + defaults: { + model: { primary: "openrouter/anthropic/claude-sonnet-4-5" }, + models: { + "openrouter/anthropic/claude-sonnet-4-5": { + compat: { + openRouterRouting: {}, + }, + }, + }, + }, + }, + }, + null, + 2, + ), + "utf-8", + ); + + const { loadConfig } = await import("./config.js"); + const cfg = loadConfig(); + + const model = cfg.agents?.defaults?.models?.["openrouter/anthropic/claude-sonnet-4-5"]; + expect(model?.compat?.openRouterRouting).toEqual({}); + }); + }); +});