model-resolver.test.ts

 1import { describe, test, expect } from "bun:test";
 2import { resolveModel } from "../src/agent/model-resolver.js";
 3import type { RumiloConfig } from "../src/config/schema.js";
 4import { ConfigError } from "../src/util/errors.js";
 5
 6// Minimal config stub for tests
 7const stubConfig: RumiloConfig = {
 8  defaults: { model: "test:m", cleanup: true },
 9  web: { model: "test:m" },
10  repo: { model: "test:m", default_depth: 1, blob_limit: "5m" },
11  custom_models: {},
12};
13
14describe("resolveModel - colon handling (issue #5)", () => {
15  test("model string with multiple colons preserves segments after second colon", () => {
16    // e.g. "openrouter:google/gemini-2.5-pro:free" should parse as
17    // provider = "openrouter", modelName = "google/gemini-2.5-pro:free"
18    // This will throw from getModel (unknown provider) but the parsed modelName
19    // should contain the full string after the first colon.
20    // We test via custom: prefix where we can control resolution.
21    const config: RumiloConfig = {
22      ...stubConfig,
23      custom_models: {
24        "name:with:colons": {
25          id: "test-id",
26          name: "test",
27          api: "openai",
28          provider: "test",
29          base_url: "http://localhost",
30          reasoning: false,
31          input: ["text"],
32          cost: { input: 0, output: 0 },
33          context_window: 1000,
34          max_tokens: 500,
35        },
36      },
37    };
38    // "custom:name:with:colons" should split as provider="custom", modelName="name:with:colons"
39    const model = resolveModel("custom:name:with:colons", config);
40    expect(model.id).toBe("test-id");
41  });
42
43  test("simple provider:model still works", () => {
44    // This will call getModel which may throw for unknown providers,
45    // but at minimum the split should be correct. Test with custom.
46    const config: RumiloConfig = {
47      ...stubConfig,
48      custom_models: {
49        "simple": {
50          id: "simple-id",
51          name: "simple",
52          api: "openai",
53          provider: "test",
54          base_url: "http://localhost",
55          reasoning: false,
56          input: ["text"],
57          cost: { input: 0, output: 0 },
58          context_window: 1000,
59          max_tokens: 500,
60        },
61      },
62    };
63    const model = resolveModel("custom:simple", config);
64    expect(model.id).toBe("simple-id");
65  });
66
67  test("rejects model string without colon", () => {
68    expect(() => resolveModel("nocodelimiter", stubConfig)).toThrow(ConfigError);
69  });
70});