singularity-forge/src/resources/extensions/sf/tests/provider-model-allow.test.ts
2026-04-30 07:41:24 +02:00

355 lines
9.9 KiB
TypeScript

import assert from "node:assert/strict";
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import {
loadEffectiveSFPreferences,
validatePreferences,
} from "../preferences.ts";
import {
filterModelsByProviderModelAllow,
isProviderAllowedByLists,
} from "../preferences-models.ts";
test("provider_model_allow: provider in allow-list and model in list passes", () => {
const models = [
{ provider: "minimax", id: "MiniMax-M2.7" },
{ provider: "minimax", id: "MiniMax-M2" },
];
const filtered = filterModelsByProviderModelAllow(models, {
minimax: ["MiniMax-M2.7"],
});
assert.deepEqual(
filtered.map((m) => `${m.provider}/${m.id}`),
["minimax/MiniMax-M2.7"],
);
});
test("provider_model_allow: provider in allow-list and model not in list is filtered", () => {
const models = [
{ provider: "minimax", id: "MiniMax-M2" },
{ provider: "zai", id: "glm-5" },
];
const filtered = filterModelsByProviderModelAllow(models, {
minimax: ["MiniMax-M2.7"],
});
assert.deepEqual(
filtered.map((m) => `${m.provider}/${m.id}`),
["zai/glm-5"],
"minimax/MiniMax-M2 is removed so selection can fall through to the next provider",
);
});
test("provider_model_allow: provider absent from allow-list is unrestricted", () => {
const models = [
{ provider: "minimax", id: "MiniMax-M2" },
{ provider: "zai", id: "glm-5" },
];
const filtered = filterModelsByProviderModelAllow(models, {
minimax: ["MiniMax-M2.7"],
});
assert.ok(filtered.some((m) => m.provider === "zai" && m.id === "glm-5"));
});
test("provider_model_allow: OpenRouter defaults to free models only", () => {
const models = [
{ provider: "openrouter", id: "qwen/qwen3-4b:free" },
{ provider: "openrouter", id: "z-ai/glm-5.1" },
{ provider: "zai", id: "glm-5.1" },
];
const filtered = filterModelsByProviderModelAllow(models, undefined);
assert.deepEqual(
filtered.map((m) => `${m.provider}/${m.id}`),
["openrouter/qwen/qwen3-4b:free", "zai/glm-5.1"],
);
});
test("provider_model_allow: supports OpenRouter free-model suffix patterns", () => {
const models = [
{ provider: "openrouter", id: "qwen/qwen3-coder:free" },
{ provider: "openrouter", id: "openai/gpt-oss-120b:free" },
{ provider: "openrouter", id: "minimax/minimax-m2.5" },
{ provider: "zai", id: "glm-4.6" },
];
const suffixFiltered = filterModelsByProviderModelAllow(models, {
openrouter: [":free"],
});
assert.deepEqual(
suffixFiltered.map((m) => `${m.provider}/${m.id}`),
[
"openrouter/qwen/qwen3-coder:free",
"openrouter/openai/gpt-oss-120b:free",
"zai/glm-4.6",
],
);
const globFiltered = filterModelsByProviderModelAllow(models, {
openrouter: ["*:free"],
});
assert.deepEqual(
globFiltered.map((m) => `${m.provider}/${m.id}`),
suffixFiltered.map((m) => `${m.provider}/${m.id}`),
);
});
test("provider_model_allow: OpenRouter paid models stay blocked even when explicitly listed", () => {
const models = [
{ provider: "openrouter", id: "z-ai/glm-5.1" },
{ provider: "openrouter", id: "qwen/qwen3-4b:free" },
];
const filtered = filterModelsByProviderModelAllow(models, {
openrouter: ["z-ai/glm-5.1", "qwen/qwen3-4b:free"],
});
assert.deepEqual(
filtered.map((m) => `${m.provider}/${m.id}`),
["openrouter/qwen/qwen3-4b:free"],
);
});
test("provider_model_allow: OpenCode defaults to free models while OpenCode Go stays unrestricted", () => {
const models = [
{ provider: "opencode", id: "big-pickle" },
{ provider: "opencode", id: "gpt-5-nano" },
{ provider: "opencode", id: "minimax-m2.5-free" },
{ provider: "opencode", id: "nemotron-3-super-free" },
{ provider: "opencode", id: "gpt-5.4" },
{ provider: "opencode", id: "kimi-k2.5" },
{ provider: "opencode-go", id: "minimax-m2.7" },
];
const filtered = filterModelsByProviderModelAllow(models, undefined);
assert.deepEqual(
filtered.map((m) => `${m.provider}/${m.id}`),
[
"opencode/big-pickle",
"opencode/gpt-5-nano",
"opencode/minimax-m2.5-free",
"opencode/nemotron-3-super-free",
"opencode-go/minimax-m2.7",
],
);
});
test("provider_model_allow: hides Xiaomi token-plan regional aliases", () => {
const models = [
{ provider: "xiaomi", id: "mimo-v2-pro" },
{ provider: "xiaomi-token-plan-ams", id: "mimo-v2-pro" },
{ provider: "xiaomi-token-plan-cn", id: "mimo-v2-pro" },
{ provider: "xiaomi-token-plan-sgp", id: "mimo-v2-pro" },
{ provider: "opencode-go", id: "mimo-v2-pro" },
];
const filtered = filterModelsByProviderModelAllow(models, undefined);
assert.deepEqual(
filtered.map((m) => `${m.provider}/${m.id}`),
["xiaomi/mimo-v2-pro", "opencode-go/mimo-v2-pro"],
);
});
test("provider_model_allow: hides Claude Code from normal selection", () => {
const models = [
{ provider: "claude-code", id: "sonnet" },
{ provider: "kimi-coding", id: "kimi-for-coding" },
];
const filtered = filterModelsByProviderModelAllow(models, undefined);
assert.deepEqual(
filtered.map((m) => `${m.provider}/${m.id}`),
["kimi-coding/kimi-for-coding"],
);
});
test("provider_model_allow: hides Mistral non-selection endpoints", () => {
const models = [
{ provider: "mistral", id: "mistral-large-latest" },
{ provider: "mistral", id: "codestral-latest" },
{ provider: "mistral", id: "mistral-embed" },
{ provider: "mistral", id: "mistral-ocr-latest" },
{ provider: "mistral", id: "voxtral-mini-tts-latest" },
{ provider: "mistral", id: "ft:codestral-latest:abc" },
];
const filtered = filterModelsByProviderModelAllow(models, undefined);
assert.deepEqual(
filtered.map((m) => `${m.provider}/${m.id}`),
[
"mistral/mistral-large-latest",
"mistral/codestral-latest",
],
);
});
test("provider_model_block: blocks matching models even when provider is otherwise unrestricted", () => {
const models = [
{ provider: "minimax", id: "MiniMax-M2.7" },
{ provider: "minimax", id: "MiniMax-M2.7-highspeed" },
{ provider: "zai", id: "glm-5.1" },
];
const filtered = filterModelsByProviderModelAllow(models, undefined, {
minimax: ["MiniMax-M2.7-highspeed"],
});
assert.deepEqual(
filtered.map((m) => `${m.provider}/${m.id}`),
["minimax/MiniMax-M2.7", "zai/glm-5.1"],
);
});
test("provider_model_block: deny wins over provider_model_allow", () => {
const models = [
{ provider: "minimax", id: "MiniMax-M2.7" },
{ provider: "minimax", id: "MiniMax-M2.7-highspeed" },
];
const filtered = filterModelsByProviderModelAllow(
models,
{ minimax: ["MiniMax-M2.7", "MiniMax-M2.7-highspeed"] },
{ minimax: ["MiniMax-M2.7"] },
);
assert.deepEqual(
filtered.map((m) => `${m.provider}/${m.id}`),
["minimax/MiniMax-M2.7-highspeed"],
);
});
test("blocked_providers: deny wins over allowed_providers", () => {
assert.equal(
isProviderAllowedByLists("zai", ["zai", "minimax"], ["zai"]),
false,
);
assert.equal(isProviderAllowedByLists("minimax", ["zai"], undefined), false);
assert.equal(isProviderAllowedByLists("minimax", undefined, ["zai"]), true);
});
test("provider_model_allow: validates shape and normalizes provider IDs", () => {
const result = validatePreferences({
provider_model_allow: {
MiniMax: [" MiniMax-M2.7 ", "MiniMax-M2.7-highspeed"],
},
provider_model_block: {
OpenRouter: [" z-ai/glm-5.1 ", ":paid"],
},
blocked_providers: [" OpenRouter ", "GOOGLE"],
});
assert.equal(result.errors.length, 0);
assert.deepEqual(result.preferences.provider_model_allow, {
minimax: ["MiniMax-M2.7", "MiniMax-M2.7-highspeed"],
});
assert.deepEqual(result.preferences.provider_model_block, {
openrouter: ["z-ai/glm-5.1", ":paid"],
});
assert.deepEqual(result.preferences.blocked_providers, [
"openrouter",
"google",
]);
});
test("provider_model_allow: rejects invalid shapes", () => {
const result = validatePreferences({
provider_model_allow: {
minimax: "MiniMax-M2.7",
zai: ["glm-5", 42],
} as any,
provider_model_block: {
minimax: "MiniMax-M2.7",
} as any,
blocked_providers: ["openrouter", 7] as any,
});
assert.ok(
result.errors.some((error) =>
error.includes("provider_model_allow.minimax"),
),
);
assert.ok(
result.errors.some((error) => error.includes("provider_model_allow.zai")),
);
assert.ok(
result.errors.some((error) =>
error.includes("provider_model_block.minimax"),
),
);
assert.ok(
result.errors.some((error) =>
error.includes("blocked_providers must be an array of strings"),
),
);
assert.equal(result.preferences.provider_model_allow, undefined);
assert.equal(result.preferences.provider_model_block, undefined);
assert.equal(result.preferences.blocked_providers, undefined);
});
test("provider_model_allow: project overrides global per provider", () => {
const originalCwd = process.cwd();
const originalSfHome = process.env.SF_HOME;
const tempProject = mkdtempSync(
join(tmpdir(), "sf-provider-model-allow-project-"),
);
const tempHome = mkdtempSync(join(tmpdir(), "sf-provider-model-allow-home-"));
try {
mkdirSync(join(tempProject, ".sf"), { recursive: true });
writeFileSync(
join(tempHome, "preferences.md"),
[
"---",
"provider_model_allow:",
" minimax:",
" - MiniMax-M2.7",
" zai:",
" - glm-5",
"---",
].join("\n"),
"utf-8",
);
writeFileSync(
join(tempProject, ".sf", "PREFERENCES.md"),
[
"---",
"provider_model_allow:",
" minimax:",
" - MiniMax-M2.7-highspeed",
"---",
].join("\n"),
"utf-8",
);
process.env.SF_HOME = tempHome;
process.chdir(tempProject);
const loaded = loadEffectiveSFPreferences();
assert.notEqual(loaded, null);
assert.deepEqual(loaded!.preferences.provider_model_allow, {
minimax: ["MiniMax-M2.7-highspeed"],
zai: ["glm-5"],
});
} finally {
process.chdir(originalCwd);
if (originalSfHome === undefined) delete process.env.SF_HOME;
else process.env.SF_HOME = originalSfHome;
rmSync(tempProject, { recursive: true, force: true });
rmSync(tempHome, { recursive: true, force: true });
}
});