summaryrefslogtreecommitdiff
path: root/spec/test_models.lua
diff options
context:
space:
mode:
Diffstat (limited to 'spec/test_models.lua')
-rw-r--r--spec/test_models.lua158
1 files changed, 158 insertions, 0 deletions
diff --git a/spec/test_models.lua b/spec/test_models.lua
new file mode 100644
index 0000000..ae01396
--- /dev/null
+++ b/spec/test_models.lua
@@ -0,0 +1,158 @@
+-- subagents/models.lua: the four catalog query forms and the profile join.
+--
+-- The fake host answers by query shape, so each case checks both what the tool
+-- asked the host for (`handle.models_queries`) and how it rendered the answer.
+
+local fake = require("spec.fake_ext")
+local models = require("subagents.models")
+
+local function has(text, needle)
+ assert(type(text) == "string", "expected a string result, got " .. type(text))
+ assert(text:find(needle, 1, true), "expected to find " .. needle .. " in:\n" .. tostring(text))
+end
+
+local function catalog(query)
+ if query.model then
+ if query.model == "anthropic:sonnet" then
+ return {
+ found = true,
+ ref = "anthropic:sonnet",
+ wire_model = "claude-sonnet-4-6",
+ reasoning_default = "medium",
+ reasoning_levels = { "low", "medium", "high" },
+ context_window = 200000,
+ max_tokens = 64000,
+ }
+ end
+ return { found = false }
+ end
+ if query.provider or query.query then
+ return {
+ matches = {
+ { ref = "anthropic:sonnet", wire_model = "claude-sonnet-4-6", reasoning_default = "medium" },
+ { ref = "anthropic:opus", wire_model = "claude-opus-4-6" },
+ },
+ truncated = true,
+ }
+ end
+ return {
+ model = "anthropic:sonnet",
+ reasoning = "medium",
+ providers = { { name = "anthropic", style = "messages", models = 7 } },
+ }
+end
+
+local function profile_set()
+ local reviewer = {
+ name = "reviewer",
+ description = "Reviews changes",
+ model = "anthropic:sonnet",
+ reasoning = "high",
+ body = "b",
+ }
+ local scout = { name = "scout", description = "", body = "b" }
+ return {
+ list = { reviewer, scout },
+ by_name = { reviewer = reviewer, scout = scout },
+ warnings = {},
+ }
+end
+
+local function with_host(fn)
+ local handle = fake.install({ models_response = catalog })
+ local ok, err = pcall(fn, handle, profile_set())
+ handle.restore()
+ if not ok then
+ error(err, 0)
+ end
+end
+
+return {
+ { "no arguments reports the inherited model and provider counts", function()
+ with_host(function(handle, profiles)
+ local text = models.handle({}, profiles)
+ has(text, "inherited model: anthropic:sonnet")
+ has(text, "inherited reasoning: medium")
+ has(text, "anthropic (messages, 7 models)")
+ assert(next(handle.models_queries[1]) == nil, "the overview query carries no fields")
+ end)
+ end },
+
+ { "an exact model lookup reports its reasoning levels", function()
+ with_host(function(handle, profiles)
+ local text = models.handle({ model = "anthropic:sonnet" }, profiles)
+ has(text, "model: anthropic:sonnet")
+ has(text, "wire model: claude-sonnet-4-6")
+ has(text, "default reasoning: medium")
+ has(text, "reasoning levels: low, medium, high")
+ has(text, "context window: 200000")
+ assert(handle.models_queries[1].model == "anthropic:sonnet")
+ end)
+ end },
+
+ { "an unknown model says so and suggests a search", function()
+ with_host(function(handle, profiles)
+ local text = models.handle({ model = "acme:turbo" }, profiles)
+ has(text, "No configured model matches 'acme:turbo'")
+ has(text, "subagents.models")
+ end)
+ end },
+
+ { "a search reports matches and says when more exist", function()
+ with_host(function(handle, profiles)
+ local text = models.handle({ query = "son" }, profiles)
+ has(text, "2 match(es):")
+ has(text, "anthropic:sonnet — wire claude-sonnet-4-6, default reasoning medium")
+ has(text, "more exist — refine the query")
+ assert(handle.models_queries[1].limit == 10, "the default limit is 10")
+ end)
+ end },
+
+ { "limit is clamped to 1..50", function()
+ with_host(function(handle, profiles)
+ models.handle({ query = "son", limit = 500 }, profiles)
+ assert(handle.models_queries[1].limit == 50, tostring(handle.models_queries[1].limit))
+ models.handle({ provider = "anthropic", limit = 0 }, profiles)
+ assert(handle.models_queries[2].limit == 1, tostring(handle.models_queries[2].limit))
+ models.handle({ query = "son", limit = 7.6 }, profiles)
+ assert(handle.models_queries[3].limit == 7, "a fractional limit is floored")
+ end)
+ end },
+
+ { "an agent with a model reports that model", function()
+ with_host(function(handle, profiles)
+ local text = models.handle({ agent = "reviewer" }, profiles)
+ has(text, "agent: reviewer")
+ has(text, "description: Reviews changes")
+ has(text, "wire model: claude-sonnet-4-6")
+ has(text, "profile reasoning: high")
+ assert(handle.models_queries[1].model == "anthropic:sonnet", "the profile model is looked up")
+ end)
+ end },
+
+ { "an agent without a model says it inherits", function()
+ with_host(function(handle, profiles)
+ local text = models.handle({ agent = "scout" }, profiles)
+ has(text, "agent: scout")
+ has(text, "model: inherits the primary model")
+ has(text, "inherited model: anthropic:sonnet")
+ assert(next(handle.models_queries[1]) == nil, "the inherited case asks for the overview")
+ end)
+ end },
+
+ { "an unknown agent names the known profiles", function()
+ with_host(function(handle, profiles)
+ local text = models.handle({ agent = "ghost" }, profiles)
+ has(text, "Error: unknown agent 'ghost'")
+ has(text, "reviewer, scout")
+ end)
+ end },
+
+ { "a non-string field is refused", function()
+ with_host(function(handle, profiles)
+ has(models.handle({ query = 12 }, profiles), "Error: `query` must be a non-empty string")
+ has(models.handle({ model = "" }, profiles), "Error: `model` must be a non-empty string")
+ assert(#handle.models_queries == 0, "a refused call never reaches the host")
+ end)
+ end },
+}