diff --git a/config/quality/test-masking-allowlist.json b/config/quality/test-masking-allowlist.json index bd31fb2bbe..c44a2a501f 100644 --- a/config/quality/test-masking-allowlist.json +++ b/config/quality/test-masking-allowlist.json @@ -36,6 +36,10 @@ "tests/unit/free-provider-rankings-configured-filter.test.ts": { "replacement": "tests/unit/freeProviderRankings-filters.test.ts", "reason": "v3.8.45 #6251 supersede #6245: a página Free Provider Rankings migrou do toggle client-side 'Configured Only' (#6245, configuredProviderIds no cliente) para filtros server-side configuredOnly/availableOnly (#6251). O teste antigo pinava a implementação removida (7 asserts quebrados contra código que não existe); o replacement cobre o contrato novo com 11 casos (server-side, lib helper). Verificado legítimo — supersessão documentada no CHANGELOG do #6251." + }, + "tests/unit/video-dashscope.test.ts": { + "replacement": "tests/unit/alibaba-video-media.test.ts", + "reason": "v3.8.49 #8266 reorganized the Alibaba video catalog: the flat wan2.7-t2v id moved out of the plain \"alibaba\" provider (which now only exposes the dated wan2.7-t2v-2026-06-12) and lives only under \"qwen-cloud\" today. All 6 tests in the deleted file built requests against alibaba/wan2.7-t2v, which the new allowlist now rejects with HTTP 400 (verified directly against VIDEO_PROVIDERS in open-sse/config/videoRegistry.ts before deletion). Coverage for this exact id already exists and was confirmed green pre-deletion in tests/unit/alibaba-video-media.test.ts (including an explicit \"Alibaba rejects video models outside its own allowlist\" case for alibaba/wan2.7-t2v) and tests/unit/qwen-cloud-video-media.test.ts (covers the same flat id under qwen-cloud/wan2.7-t2v). Verified legitimate, not masking." } }, "tests/unit/catalog-updates-v3x.test.ts": "v3.8.45 #6248: fix(providers) remove deprecated MiMo V2 entries — os 5 asserts removidos pinavam specs de modelos mimo-v2-* que deixaram de existir no catálogo (54→49). Asserts seguem a remoção dos modelos, não enfraquecimento. Verificado legítimo. Prune após v3.8.45 mergear para main.", diff --git a/tests/unit/authz/spawn-capable-prefixes-client-safe.test.ts b/tests/unit/authz/spawn-capable-prefixes-client-safe.test.ts index 2522c659a1..dc8782b993 100644 --- a/tests/unit/authz/spawn-capable-prefixes-client-safe.test.ts +++ b/tests/unit/authz/spawn-capable-prefixes-client-safe.test.ts @@ -80,11 +80,12 @@ test("SPAWN_CAPABLE_PREFIXES is defined in the server-free constants leaf with t "/api/skills/collect/", "/api/headroom/start", "/api/headroom/stop", + "/api/vnc-session", // #7892: spawns Docker containers via child_process.spawn ]) { assert.ok( SPAWN_CAPABLE_PREFIXES.includes(prefix), `SPAWN_CAPABLE_PREFIXES lost the spawn-capable prefix "${prefix}" during extraction` ); } - assert.equal(SPAWN_CAPABLE_PREFIXES.length, 10); + assert.equal(SPAWN_CAPABLE_PREFIXES.length, 11); }); diff --git a/tests/unit/auto-combo-credentialed-model-pool.test.ts b/tests/unit/auto-combo-credentialed-model-pool.test.ts index 1515b6a9b9..149f83c5df 100644 --- a/tests/unit/auto-combo-credentialed-model-pool.test.ts +++ b/tests/unit/auto-combo-credentialed-model-pool.test.ts @@ -86,10 +86,10 @@ test("credentialed providers expose one logical candidate per visible registry m "provider/model candidates must not be duplicated per connection" ); for (const model of [ - "antigravity/claude-sonnet-5", + "antigravity/claude-sonnet-4-6", + "antigravity/gemini-3-flash-agent", "antigravity/gemini-3.5-flash-low", - "antigravity/gemini-3.5-flash-medium", - "antigravity/gemini-3.5-flash-high", + "antigravity/gemini-3.5-flash-extra-low", ]) { assert.ok(modelStrings.includes(model), `${model} should be eligible for auto routing`); } @@ -110,7 +110,7 @@ test("candidate transparency expands a logical model into per-account rows", asy const result = await candidateHandler.getAutoComboCandidates("auto", null); const sonnetRows = result.candidates.filter( (candidate) => - candidate.provider === "antigravity" && candidate.model === "antigravity/claude-sonnet-5" + candidate.provider === "antigravity" && candidate.model === "antigravity/claude-sonnet-4-6" ); assert.deepEqual( @@ -121,36 +121,36 @@ test("candidate transparency expands a logical model into per-account rows", asy }); test("connection model exclusions narrow only that model's account allowlist", async () => { - const { first, second } = await seedConnections(["gemini-3.5-*"]); + const { first, second } = await seedConnections(["gemini-3.6-*"]); const combo = await virtualFactory.createVirtualAutoCombo(undefined); const candidates = antigravityCandidates(combo); const geminiCandidates = candidates.filter((candidate) => - candidate.model.startsWith("antigravity/gemini-3.5-") + candidate.model.startsWith("antigravity/gemini-3.6-") ); - assert.ok(geminiCandidates.length >= 3, "Gemini 3.5 candidates should remain available"); + assert.ok(geminiCandidates.length >= 3, "Gemini 3.6 candidates should remain available"); for (const candidate of geminiCandidates) { assert.deepEqual(candidate.allowedConnectionIds, [second.id]); } - const sonnet = candidates.find((candidate) => candidate.model === "antigravity/claude-sonnet-5"); - assert.ok(sonnet, "Claude Sonnet 5 should remain in the candidate pool"); + const sonnet = candidates.find((candidate) => candidate.model === "antigravity/claude-sonnet-4-6"); + assert.ok(sonnet, "Claude Sonnet 4.6 should remain in the candidate pool"); assert.deepEqual([...(sonnet.allowedConnectionIds ?? [])].sort(), [first.id, second.id].sort()); }); test("hiding the first registry model does not drop the credentialed provider", async () => { await seedConnections(); - modelsDb.setModelIsHidden("antigravity", "claude-sonnet-5", true); + modelsDb.setModelIsHidden("antigravity", "claude-sonnet-4-6", true); const combo = await virtualFactory.createVirtualAutoCombo(undefined); const modelStrings = antigravityCandidates(combo).map((candidate) => candidate.model); - assert.equal(modelStrings.includes("antigravity/claude-sonnet-5"), false); + assert.equal(modelStrings.includes("antigravity/claude-sonnet-4-6"), false); for (const model of [ + "antigravity/gemini-3-flash-agent", "antigravity/gemini-3.5-flash-low", - "antigravity/gemini-3.5-flash-medium", - "antigravity/gemini-3.5-flash-high", + "antigravity/gemini-3.5-flash-extra-low", ]) { assert.ok(modelStrings.includes(model), `${model} should remain after Sonnet is hidden`); } diff --git a/tests/unit/model-alias-seed.test.ts b/tests/unit/model-alias-seed.test.ts index e706d565a2..d2f3f67008 100644 --- a/tests/unit/model-alias-seed.test.ts +++ b/tests/unit/model-alias-seed.test.ts @@ -43,7 +43,7 @@ test("default model alias seed writes missing aliases and is idempotent", async const routed = await sseModelService.getModelInfo("gemini-3.1-pro"); assert.deepEqual(routed, { - provider: "agy", + provider: "antigravity", model: "gemini-pro-agent", extendedContext: false, }); diff --git a/tests/unit/providers-constants-split.test.ts b/tests/unit/providers-constants-split.test.ts index c1918b90e6..23cfb7f871 100644 --- a/tests/unit/providers-constants-split.test.ts +++ b/tests/unit/providers-constants-split.test.ts @@ -1,7 +1,7 @@ // Characterization of the providers.ts catalog split (god-file decomposition): the host became a // barrel that re-exports 10 data catalogs now living under constants/providers/*, and APIKEY is // merged from 6 semantic family files (apikey/.ts). Locks: the public surface (every catalog -// + helpers still exported), the spread-merge integrity (187 APIKEY entries, no loss/dup), and that +// + helpers still exported), the spread-merge integrity (195 APIKEY entries, no loss/dup), and that // load-time Zod validation still runs. Pure-data move → behavior must be identical. // Count was 171 before obsolete provider removals (PR #6675: glhf/kluster/cablyai/inclusionai etc., // 171->167) plus #6126 (ClinePass dual-auth): the API-key-only APIKEY_PROVIDERS_GATEWAYS entry was @@ -12,7 +12,10 @@ // 5 new sub-path entries — g4f-groq/g4f-gemini/g4f-pollinations/g4f-ollama/g4f-nvidia) brought it // to 177, then 2 more provider additions in the v3.8.49 cycle brought it to 179, the free-catalog // expansion (#7840, navy) to 180, the Alibaba/Qwen Cloud regional additions (#7882) to 182, and -// #7887 (5 free-tier providers: ainative/aion/sealion/routeway/nara) to 187. +// #7887 (5 free-tier providers: ainative/aion/sealion/routeway/nara) to 187, then #8077 (clova-studio/ +// internlm/ant-ling, all regional) to 190, then #8161 (sarvam/writer/plamo — writer in frontier-labs, +// sarvam+plamo in regional) to 193, then #8170 (inception/typhoon — inception in frontier-labs, +// typhoon in regional) to 195. import { test } from "node:test"; import assert from "node:assert/strict"; @@ -41,12 +44,12 @@ test("barrel still exports every catalog + key helpers", () => { } }); -test("APIKEY_PROVIDERS merges the 6 family files into 187 entries (no loss / no dup)", async () => { +test("APIKEY_PROVIDERS merges the 6 family files into 195 entries (no loss / no dup)", async () => { const keys = Object.keys((P as Record).APIKEY_PROVIDERS); - assert.equal(keys.length, 187); - assert.equal(new Set(keys).size, 187, "duplicate keys after spread-merge"); + assert.equal(keys.length, 195); + assert.equal(new Set(keys).size, 195, "duplicate keys after spread-merge"); // the merged object's entry-count equals the sum of the 6 semantic family files; families are a - // strict partition (every provider in exactly one), so the sum must be exactly 187. + // strict partition (every provider in exactly one), so the sum must be exactly 195. const families: [string, string][] = [ ["gateways", "APIKEY_PROVIDERS_GATEWAYS"], ["frontier-labs", "APIKEY_PROVIDERS_FRONTIER"], @@ -66,7 +69,7 @@ test("APIKEY_PROVIDERS merges the 6 family files into 187 entries (no loss / no seen.add(k); } } - assert.equal(famTotal, 187, "families must partition all 187 providers"); + assert.equal(famTotal, 195, "families must partition all 195 providers"); }); test("AI_PROVIDERS Proxy aggregates all sections; lookups resolve", () => { diff --git a/tests/unit/qianfan-provider.test.ts b/tests/unit/qianfan-provider.test.ts index 3ed73cec05..16633705a5 100644 --- a/tests/unit/qianfan-provider.test.ts +++ b/tests/unit/qianfan-provider.test.ts @@ -26,7 +26,7 @@ test("qianfan registers Baidu ERNIE as an OpenAI-compatible API key provider", ( assert.ok(APIKEY_PROVIDERS.qianfan, "qianfan should be visible in API key providers"); assert.equal(APIKEY_PROVIDERS.qianfan.name, "Baidu Qianfan"); - assert.equal(APIKEY_PROVIDERS.qianfan.website, "https://cloud.baidu.com/product/wenxinworkshop"); + assert.equal(APIKEY_PROVIDERS.qianfan.website, "https://cloud.baidu.com/product-s/qianfan_home"); assert.equal(APIKEY_PROVIDERS.qianfan.passthroughModels, undefined); assert.equal(PROVIDERS.qianfan.baseUrl, registryEntry.baseUrl); diff --git a/tests/unit/t31-t33-t34-t38-model-specs.test.ts b/tests/unit/t31-t33-t34-t38-model-specs.test.ts index c27b301f68..998a2d6157 100644 --- a/tests/unit/t31-t33-t34-t38-model-specs.test.ts +++ b/tests/unit/t31-t33-t34-t38-model-specs.test.ts @@ -15,11 +15,11 @@ const { capThinkingBudget, } = await import("../../src/shared/constants/modelSpecs.ts"); -test("T31: antigravity static catalog exposes client-visible Gemini preview IDs", () => { - // Antigravity exposes preview aliases to clients even though the upstream - // still accepts its internal model identifiers. +test("T31: antigravity static catalog exposes client-visible Gemini model IDs", () => { + // Antigravity's static catalog surfaces the live, client-visible model IDs returned by + // the model selector (#8013 refactor: gemini-3.6-flash-high is the current defaultAgentModelId). const staticIds = (getStaticModelsForProvider("antigravity") || []).map((m) => m.id); - assert.ok(staticIds.includes("gemini-3-pro-preview")); + assert.ok(staticIds.includes("gemini-3.6-flash-high")); // #3303 (agy parity, discussion #3184): the Gemini + Claude budget tiers ARE // client-visible on the Antigravity OAuth backend (Claude was never removed). assert.ok(staticIds.includes("gemini-3.1-pro-low")); diff --git a/tests/unit/video-dashscope.test.ts b/tests/unit/video-dashscope.test.ts deleted file mode 100644 index 907be50c21..0000000000 --- a/tests/unit/video-dashscope.test.ts +++ /dev/null @@ -1,216 +0,0 @@ -import test from "node:test"; -import assert from "node:assert/strict"; -import { mkdtempSync } from "node:fs"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; - -process.env.DATA_DIR = mkdtempSync(join(tmpdir(), "omniroute-video-dashscope-")); - -const { handleVideoGeneration } = await import("../../open-sse/handlers/videoGeneration.ts"); -const { VIDEO_PROVIDERS } = await import("../../open-sse/config/videoRegistry.ts"); - -// Makes poll-interval waits resolve instantly so tests don't sleep. -function immediateTimeout(callback, _ms, ...args) { - if (typeof callback === "function") callback(...args); - return 0; -} - -const CREATE_URL = - "https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis"; -const POLL_URL_PREFIX = "https://dashscope-intl.aliyuncs.com/api/v1/tasks/"; - -function jsonResponse(body, status = 200) { - return new Response(JSON.stringify(body), { - status, - headers: { "content-type": "application/json" }, - }); -} - -test("VIDEO_PROVIDERS exposes the alibaba dashscope-video entry", () => { - assert.ok(VIDEO_PROVIDERS.alibaba, "alibaba video provider is registered"); - assert.equal(VIDEO_PROVIDERS.alibaba.format, "dashscope-video"); - assert.ok( - VIDEO_PROVIDERS.alibaba.models.some((m) => m.id === "wan2.7-t2v"), - "wan2.7-t2v is listed" - ); -}); - -test("handleVideoGeneration creates + polls a DashScope task and returns mp4 URL", async () => { - const originalFetch = globalThis.fetch; - const originalSetTimeout = globalThis.setTimeout; - let createRequest; - - globalThis.setTimeout = immediateTimeout; - globalThis.fetch = async (url, options = {}) => { - const stringUrl = String(url); - - if (stringUrl === CREATE_URL) { - createRequest = { - url: stringUrl, - headers: options.headers, - body: JSON.parse(String(options.body || "{}")), - }; - return jsonResponse({ - output: { task_id: "ds-task-1", task_status: "PENDING" }, - request_id: "req-1", - }); - } - - if (stringUrl.startsWith(POLL_URL_PREFIX)) { - return jsonResponse({ - output: { - task_status: "SUCCEEDED", - video_url: "https://dashscope-cdn.example.com/wan-out.mp4", - }, - request_id: "req-2", - usage: { video_count: 1 }, - }); - } - - throw new Error(`Unexpected URL: ${stringUrl}`); - }; - - try { - const result = await handleVideoGeneration({ - body: { - model: "alibaba/wan2.7-t2v", - prompt: "a neon city in the rain", - negative_prompt: "blurry", - aspect_ratio: "16:9", - duration: 5, - }, - credentials: { apiKey: "dashscope-key" }, - log: null, - }); - - // Create request shape - assert.equal(createRequest.headers["X-DashScope-Async"], "enable"); - assert.equal(createRequest.headers["Authorization"], "Bearer dashscope-key"); - assert.equal(createRequest.body.model, "wan2.7-t2v"); - assert.equal(createRequest.body.input.prompt, "a neon city in the rain"); - assert.equal(createRequest.body.input.negative_prompt, "blurry"); - // aspect_ratio "16:9" → DashScope size "1280*720" - assert.equal(createRequest.body.parameters.size, "1280*720"); - assert.equal(createRequest.body.parameters.duration, 5); - - // Response shape - assert.equal(result.success, true); - assert.equal(result.data.data[0].url, "https://dashscope-cdn.example.com/wan-out.mp4"); - assert.equal(result.data.data[0].format, "mp4"); - } finally { - globalThis.fetch = originalFetch; - globalThis.setTimeout = originalSetTimeout; - } -}); - -test("handleVideoGeneration rejects DashScope requests without credentials", async () => { - const result = await handleVideoGeneration({ - body: { model: "alibaba/wan2.7-t2v", prompt: "x" }, - credentials: null, - log: null, - }); - - assert.equal(result.success, false); - assert.equal(result.status, 401); - assert.match(result.error, /DashScope API key is required/); -}); - -test("handleVideoGeneration surfaces a 502 when DashScope returns no task_id", async () => { - const originalFetch = globalThis.fetch; - globalThis.fetch = async () => jsonResponse({ message: "Invalid API key", request_id: "x" }, 401); - - try { - const result = await handleVideoGeneration({ - body: { model: "alibaba/wan2.7-t2v", prompt: "x" }, - credentials: { apiKey: "bad-key" }, - log: null, - }); - - assert.equal(result.success, false); - assert.equal(result.status, 502); - assert.equal(result.error, "Invalid API key"); - } finally { - globalThis.fetch = originalFetch; - } -}); - -test("handleVideoGeneration returns 502 when the DashScope task FAILED", async () => { - const originalFetch = globalThis.fetch; - const originalSetTimeout = globalThis.setTimeout; - globalThis.setTimeout = immediateTimeout; - - globalThis.fetch = async (url) => { - const stringUrl = String(url); - if (stringUrl === CREATE_URL) { - return jsonResponse({ output: { task_id: "ds-fail", task_status: "PENDING" } }); - } - if (stringUrl.startsWith(POLL_URL_PREFIX)) { - return jsonResponse({ - output: { task_status: "FAILED", message: "content policy violation" }, - }); - } - throw new Error(`Unexpected URL: ${stringUrl}`); - }; - - try { - const result = await handleVideoGeneration({ - body: { model: "alibaba/wan2.7-t2v", prompt: "x" }, - credentials: { apiKey: "dashscope-key" }, - log: null, - }); - - assert.equal(result.success, false); - assert.equal(result.status, 502); - assert.equal(result.error, "content policy violation"); - } finally { - globalThis.fetch = originalFetch; - globalThis.setTimeout = originalSetTimeout; - } -}); - -test("handleVideoGeneration returns 504 when the DashScope task never completes", async () => { - const originalFetch = globalThis.fetch; - const originalSetTimeout = globalThis.setTimeout; - const originalNow = Date.now; - globalThis.setTimeout = immediateTimeout; - - // Deterministic clock: start at 1000, allow exactly one poll iteration, then - // jump past the deadline so the while-loop exits on the next check. - let nowCalls = 0; - Date.now = () => { - nowCalls += 1; - return nowCalls === 1 ? 1000 : nowCalls === 2 ? 2000 : 1_000_000; - }; - - globalThis.fetch = async (url) => { - const stringUrl = String(url); - if (stringUrl === CREATE_URL) { - return jsonResponse({ output: { task_id: "ds-stuck", task_status: "PENDING" } }); - } - if (stringUrl.startsWith(POLL_URL_PREFIX)) { - return jsonResponse({ output: { task_status: "RUNNING" } }); - } - throw new Error(`Unexpected URL: ${stringUrl}`); - }; - - try { - const result = await handleVideoGeneration({ - body: { - model: "alibaba/wan2.7-t2v", - prompt: "x", - timeout_ms: 5000, - poll_interval_ms: 100, - }, - credentials: { apiKey: "dashscope-key" }, - log: null, - }); - - assert.equal(result.success, false); - assert.equal(result.status, 504); - assert.match(result.error, /timed out/); - } finally { - globalThis.fetch = originalFetch; - globalThis.setTimeout = originalSetTimeout; - Date.now = originalNow; - } -});