From a459dfd22116e4bad82510132bcf82a75244dbb1 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Tue, 18 Aug 2026 00:16:46 -0300 Subject: [PATCH] test(sse): golden characterization of the executor map before the R0.3 registry refactor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Freezes the 137-entry provider-id → executor mapping (class, provider identity, backing PROVIDERS config), the no-shared-instances invariant, and the getExecutor() dispatch rules (memoized DefaultExecutor fallback, cloud-agent guard #6699, search-provider guard #10274) as stable JSON snapshots. The upcoming ExecutorRegistry must keep both snapshots byte-identical. --- tests/snapshots/executors/dispatch-rules.json | 86 +++ tests/snapshots/executors/executor-map.json | 691 ++++++++++++++++++ tests/unit/executor-map-golden.test.ts | 134 ++++ 3 files changed, 911 insertions(+) create mode 100644 tests/snapshots/executors/dispatch-rules.json create mode 100644 tests/snapshots/executors/executor-map.json create mode 100644 tests/unit/executor-map-golden.test.ts diff --git a/tests/snapshots/executors/dispatch-rules.json b/tests/snapshots/executors/dispatch-rules.json new file mode 100644 index 0000000000..b8f4aca6ed --- /dev/null +++ b/tests/snapshots/executors/dispatch-rules.json @@ -0,0 +1,86 @@ +{ + "cloudAgentGuard": { + "jules": { + "message": "Provider \"jules\" is a cloud-agent provider and does not support direct chat completions; use the Cloud Agents task API instead.", + "status": 400, + "throws": true + } + }, + "fallback": { + "className": "DefaultExecutor", + "configSource": "openai", + "provider": "golden-test-unknown-provider" + }, + "searchGuard": { + "brave-search": { + "message": "Provider \"brave-search\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.", + "status": 400, + "throws": true + }, + "duckduckgo-free": { + "message": "Provider \"duckduckgo-free\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.", + "status": 400, + "throws": true + }, + "exa-search": { + "message": "Provider \"exa-search\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.", + "status": 400, + "throws": true + }, + "firecrawl": { + "message": "Provider \"firecrawl\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.", + "status": 400, + "throws": true + }, + "google-pse-search": { + "message": "Provider \"google-pse-search\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.", + "status": 400, + "throws": true + }, + "linkup-search": { + "message": "Provider \"linkup-search\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.", + "status": 400, + "throws": true + }, + "ollama-search": { + "message": "Provider \"ollama-search\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.", + "status": 400, + "throws": true + }, + "perplexity-search": { + "message": "Provider \"perplexity-search\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.", + "status": 400, + "throws": true + }, + "searchapi-search": { + "message": "Provider \"searchapi-search\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.", + "status": 400, + "throws": true + }, + "searxng-search": { + "message": "Provider \"searxng-search\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.", + "status": 400, + "throws": true + }, + "serper-search": { + "message": "Provider \"serper-search\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.", + "status": 400, + "throws": true + }, + "tavily-search": { + "message": "Provider \"tavily-search\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.", + "status": 400, + "throws": true + }, + "youcom-search": { + "message": "Provider \"youcom-search\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.", + "status": 400, + "throws": true + }, + "zai-search": { + "message": "Provider \"zai-search\" is a search provider and does not support chat completions; use the /v1/search endpoint instead.", + "status": 400, + "throws": true + } + } +} diff --git a/tests/snapshots/executors/executor-map.json b/tests/snapshots/executors/executor-map.json new file mode 100644 index 0000000000..369aadf0fb --- /dev/null +++ b/tests/snapshots/executors/executor-map.json @@ -0,0 +1,691 @@ +{ + "entries": { + "9router": { + "className": "NineRouterExecutor", + "configSource": "", + "provider": "9router" + }, + "adapta-web": { + "className": "AdaptaWebExecutor", + "configSource": "", + "provider": "adapta-web" + }, + "adobe-firefly": { + "className": "AdobeFireflyExecutor", + "configSource": "", + "provider": "adobe-firefly" + }, + "adp-web": { + "className": "AdaptaWebExecutor", + "configSource": "", + "provider": "adapta-web" + }, + "agy": { + "className": "AntigravityExecutor", + "configSource": "antigravity", + "provider": "antigravity" + }, + "amazon-q": { + "className": "KiroExecutor", + "configSource": "kiro", + "provider": "amazon-q" + }, + "antigravity": { + "className": "AntigravityExecutor", + "configSource": "antigravity", + "provider": "antigravity" + }, + "auggie": { + "className": "AuggieExecutor", + "configSource": "", + "provider": "auggie" + }, + "azure-ai": { + "className": "AzureAiExecutor", + "configSource": "openai", + "provider": "azure-ai" + }, + "azure-openai": { + "className": "AzureOpenAIExecutor", + "configSource": "openai", + "provider": "azure-openai" + }, + "bb-web": { + "className": "BlackboxWebExecutor", + "configSource": "", + "provider": "blackbox-web" + }, + "bedrock": { + "className": "BedrockExecutor", + "configSource": "bedrock", + "provider": "bedrock" + }, + "blackbox-web": { + "className": "BlackboxWebExecutor", + "configSource": "", + "provider": "blackbox-web" + }, + "cbcn": { + "className": "CodeBuddyCnExecutor", + "configSource": "codebuddy-cn", + "provider": "codebuddy-cn" + }, + "cf": { + "className": "CloudflareAIExecutor", + "configSource": "cloudflare-ai", + "provider": "cloudflare-ai" + }, + "cgpt-codex": { + "className": "ChatGptWebCodexExecutor", + "configSource": "", + "provider": "chatgpt-web-codex" + }, + "cgpt-web": { + "className": "ChatGptWebExecutor", + "configSource": "", + "provider": "chatgpt-web" + }, + "chatgpt-web": { + "className": "ChatGptWebExecutor", + "configSource": "", + "provider": "chatgpt-web" + }, + "chatgpt-web-codex": { + "className": "ChatGptWebCodexExecutor", + "configSource": "", + "provider": "chatgpt-web-codex" + }, + "cheaperinference": { + "className": "CheaperInferenceExecutor", + "configSource": "cheaperinference", + "provider": "cheaperinference" + }, + "chipotle": { + "className": "ChipotleExecutor", + "configSource": "", + "provider": "chipotle" + }, + "cinf": { + "className": "CheaperInferenceExecutor", + "configSource": "cheaperinference", + "provider": "cheaperinference" + }, + "claude-web": { + "className": "ClaudeWebExecutor", + "configSource": "", + "provider": "claude-web" + }, + "cliproxyapi": { + "className": "CliproxyapiExecutor", + "configSource": "", + "provider": "cliproxyapi" + }, + "cloudflare-ai": { + "className": "CloudflareAIExecutor", + "configSource": "cloudflare-ai", + "provider": "cloudflare-ai" + }, + "cmd": { + "className": "CommandCodeExecutor", + "configSource": "", + "provider": "command-code" + }, + "cnl": { + "className": "ConolWebExecutor", + "configSource": "", + "provider": "conol-web" + }, + "codebuddy-cn": { + "className": "CodeBuddyCnExecutor", + "configSource": "codebuddy-cn", + "provider": "codebuddy-cn" + }, + "codex": { + "className": "CodexExecutor", + "configSource": "codex", + "provider": "codex" + }, + "command-code": { + "className": "CommandCodeExecutor", + "configSource": "", + "provider": "command-code" + }, + "conol-web": { + "className": "ConolWebExecutor", + "configSource": "", + "provider": "conol-web" + }, + "copilot": { + "className": "CopilotWebExecutor", + "configSource": "", + "provider": "copilot-web" + }, + "copilot-m365-web": { + "className": "CopilotM365WebExecutor", + "configSource": "", + "provider": "copilot-m365-web" + }, + "copilot-web": { + "className": "CopilotWebExecutor", + "configSource": "", + "provider": "copilot-web" + }, + "cpa": { + "className": "CliproxyapiExecutor", + "configSource": "", + "provider": "cliproxyapi" + }, + "cu": { + "className": "CursorExecutor", + "configSource": "cursor", + "provider": "cursor" + }, + "cursor": { + "className": "CursorExecutor", + "configSource": "cursor", + "provider": "cursor" + }, + "cw-web": { + "className": "ClaudeWebExecutor", + "configSource": "", + "provider": "claude-web" + }, + "dario": { + "className": "DarioExecutor", + "configSource": "", + "provider": "dario" + }, + "db": { + "className": "DoubaoWebExecutor", + "configSource": "", + "provider": "doubao-web" + }, + "ddgw": { + "className": "DuckDuckGoWebExecutor", + "configSource": "", + "provider": "duckduckgo-web" + }, + "deepseek-web": { + "className": "DeepSeekWebWithAutoRefreshExecutor", + "configSource": "", + "provider": "deepseek-web" + }, + "devin": { + "className": "DevinCliExecutor", + "configSource": "", + "provider": "devin-cli" + }, + "devin-cli": { + "className": "DevinCliExecutor", + "configSource": "", + "provider": "devin-cli" + }, + "devin-cli-agentic": { + "className": "DevinCliAgenticExecutor", + "configSource": "", + "provider": "devin-cli-agentic" + }, + "devin-desktop": { + "className": "DevinDesktopExecutor", + "configSource": "devin-desktop", + "provider": "devin-desktop" + }, + "doubao-web": { + "className": "DoubaoWebExecutor", + "configSource": "", + "provider": "doubao-web" + }, + "dr": { + "className": "DarioExecutor", + "configSource": "", + "provider": "dario" + }, + "ds-web": { + "className": "DeepSeekWebWithAutoRefreshExecutor", + "configSource": "", + "provider": "deepseek-web" + }, + "duckduckgo-web": { + "className": "DuckDuckGoWebExecutor", + "configSource": "", + "provider": "duckduckgo-web" + }, + "felo": { + "className": "FeloWebExecutor", + "configSource": "", + "provider": "felo-web" + }, + "felo-web": { + "className": "FeloWebExecutor", + "configSource": "", + "provider": "felo-web" + }, + "firefly": { + "className": "AdobeFireflyExecutor", + "configSource": "", + "provider": "adobe-firefly" + }, + "gc": { + "className": "GrokCliExecutor", + "configSource": "grok-cli", + "provider": "grok-cli" + }, + "gembiz": { + "className": "GeminiBusinessExecutor", + "configSource": "", + "provider": "gemini-business" + }, + "gemini-business": { + "className": "GeminiBusinessExecutor", + "configSource": "", + "provider": "gemini-business" + }, + "gemini-web": { + "className": "GeminiWebExecutor", + "configSource": "", + "provider": "gemini-web" + }, + "ghe-copilot": { + "className": "GheCopilotExecutor", + "configSource": "", + "provider": "ghe-copilot" + }, + "github": { + "className": "GithubExecutor", + "configSource": "github", + "provider": "github" + }, + "gitlab": { + "className": "GitlabExecutor", + "configSource": "", + "provider": "gitlab" + }, + "gitlab-duo": { + "className": "GitlabExecutor", + "configSource": "", + "provider": "gitlab-duo" + }, + "glm": { + "className": "GlmExecutor", + "configSource": "glm", + "provider": "glm" + }, + "glm-cn": { + "className": "GlmExecutor", + "configSource": "glm-cn", + "provider": "glm-cn" + }, + "glmt": { + "className": "GlmExecutor", + "configSource": "glmt", + "provider": "glmt" + }, + "grok-cli": { + "className": "GrokCliExecutor", + "configSource": "grok-cli", + "provider": "grok-cli" + }, + "grok-web": { + "className": "GrokWebExecutor", + "configSource": "", + "provider": "grok-web" + }, + "gweb": { + "className": "GeminiWebExecutor", + "configSource": "", + "provider": "gemini-web" + }, + "ha": { + "className": "HyperAgentExecutor", + "configSource": "", + "provider": "hyperagent" + }, + "hailuo-web": { + "className": "HailuoWebExecutor", + "configSource": "", + "provider": "hailuo-web" + }, + "hc": { + "className": "HuggingChatExecutor", + "configSource": "", + "provider": "huggingchat" + }, + "huggingchat": { + "className": "HuggingChatExecutor", + "configSource": "", + "provider": "huggingchat" + }, + "hyperagent": { + "className": "HyperAgentExecutor", + "configSource": "", + "provider": "hyperagent" + }, + "in-ai": { + "className": "InnerAiExecutor", + "configSource": "", + "provider": "inner-ai" + }, + "inner-ai": { + "className": "InnerAiExecutor", + "configSource": "", + "provider": "inner-ai" + }, + "kimi": { + "className": "MoonshotExecutor", + "configSource": "kimi", + "provider": "kimi" + }, + "kimi-coding": { + "className": "KimiExecutor", + "configSource": "kimi-coding", + "provider": "kimi-coding" + }, + "kimi-coding-apikey": { + "className": "KimiExecutor", + "configSource": "kimi-coding-apikey", + "provider": "kimi-coding-apikey" + }, + "kimi-web": { + "className": "KimiWebExecutor", + "configSource": "", + "provider": "kimi-web" + }, + "kiro": { + "className": "KiroExecutor", + "configSource": "kiro", + "provider": "kiro" + }, + "lma": { + "className": "LMArenaExecutor", + "configSource": "", + "provider": "lmarena" + }, + "lmarena": { + "className": "LMArenaExecutor", + "configSource": "", + "provider": "lmarena" + }, + "mcode": { + "className": "MimocodeExecutor", + "configSource": "", + "provider": "mimocode" + }, + "microsoft-designer-web": { + "className": "MicrosoftDesignerWebExecutor", + "configSource": "", + "provider": "microsoft-designer-web" + }, + "mimocode": { + "className": "MimocodeExecutor", + "configSource": "", + "provider": "mimocode" + }, + "moonshot": { + "className": "MoonshotExecutor", + "configSource": "moonshot", + "provider": "moonshot" + }, + "ms-web": { + "className": "MuseSparkWebExecutor", + "configSource": "", + "provider": "muse-spark-web" + }, + "msdesigner": { + "className": "MicrosoftDesignerWebExecutor", + "configSource": "", + "provider": "microsoft-designer-web" + }, + "muse-spark-web": { + "className": "MuseSparkWebExecutor", + "configSource": "", + "provider": "muse-spark-web" + }, + "nlpcloud": { + "className": "NlpCloudExecutor", + "configSource": "nlpcloud", + "provider": "nlpcloud" + }, + "notion-web": { + "className": "NotionWebExecutor", + "configSource": "", + "provider": "notion-web" + }, + "nr": { + "className": "NineRouterExecutor", + "configSource": "", + "provider": "9router" + }, + "nw": { + "className": "NotionWebExecutor", + "configSource": "", + "provider": "notion-web" + }, + "opencode": { + "className": "OpencodeExecutor", + "configSource": "opencode-zen", + "provider": "opencode-zen" + }, + "opencode-go": { + "className": "OpencodeExecutor", + "configSource": "opencode-go", + "provider": "opencode-go" + }, + "opencode-zen": { + "className": "OpencodeExecutor", + "configSource": "opencode-zen", + "provider": "opencode-zen" + }, + "pepper": { + "className": "ChipotleExecutor", + "configSource": "", + "provider": "chipotle" + }, + "perplexity-web": { + "className": "PerplexityWebExecutor", + "configSource": "", + "provider": "perplexity-web" + }, + "poe-web": { + "className": "PoeWebExecutor", + "configSource": "", + "provider": "poe-web" + }, + "pol": { + "className": "PollinationsExecutor", + "configSource": "pollinations", + "provider": "pollinations" + }, + "pollinations": { + "className": "PollinationsExecutor", + "configSource": "pollinations", + "provider": "pollinations" + }, + "pplx-web": { + "className": "PerplexityWebExecutor", + "configSource": "", + "provider": "perplexity-web" + }, + "pql": { + "className": "PromptQlExecutor", + "configSource": "", + "provider": "promptql" + }, + "promptql": { + "className": "PromptQlExecutor", + "configSource": "", + "provider": "promptql" + }, + "qoder": { + "className": "QoderExecutor", + "configSource": "qoder", + "provider": "qoder" + }, + "qw": { + "className": "QwenWebExecutor", + "configSource": "", + "provider": "qwen-web" + }, + "qwen-web": { + "className": "QwenWebExecutor", + "configSource": "", + "provider": "qwen-web" + }, + "raycast": { + "className": "RaycastExecutor", + "configSource": "raycast", + "provider": "raycast" + }, + "rc": { + "className": "RaycastExecutor", + "configSource": "raycast", + "provider": "raycast" + }, + "t3-web": { + "className": "T3ChatWebExecutor", + "configSource": "", + "provider": "t3-web" + }, + "t3chat": { + "className": "T3ChatWebExecutor", + "configSource": "", + "provider": "t3-web" + }, + "tasw": { + "className": "TencentAIStudioWebExecutor", + "configSource": "", + "provider": "tencent-aistudio-web" + }, + "tcw": { + "className": "TinyCmsExecutor", + "configSource": "", + "provider": "tinycms-web" + }, + "tencent-aistudio-web": { + "className": "TencentAIStudioWebExecutor", + "configSource": "", + "provider": "tencent-aistudio-web" + }, + "theoldllm": { + "className": "TheOldLlmExecutor", + "configSource": "", + "provider": "theoldllm" + }, + "tinycms-web": { + "className": "TinyCmsExecutor", + "configSource": "", + "provider": "tinycms-web" + }, + "tllm": { + "className": "TheOldLlmExecutor", + "configSource": "", + "provider": "theoldllm" + }, + "trae": { + "className": "TraeExecutor", + "configSource": "trae", + "provider": "trae" + }, + "v0": { + "className": "V0VercelWebExecutor", + "configSource": "", + "provider": "v0-vercel-web" + }, + "v0-vercel-web": { + "className": "V0VercelWebExecutor", + "configSource": "", + "provider": "v0-vercel-web" + }, + "ven": { + "className": "VeniceWebExecutor", + "configSource": "", + "provider": "venice-web" + }, + "venice-web": { + "className": "VeniceWebExecutor", + "configSource": "", + "provider": "venice-web" + }, + "veo-free": { + "className": "VeoAIFreeWebExecutor", + "configSource": "", + "provider": "veoaifree-web" + }, + "veoaifree-web": { + "className": "VeoAIFreeWebExecutor", + "configSource": "", + "provider": "veoaifree-web" + }, + "vertex": { + "className": "VertexExecutor", + "configSource": "vertex", + "provider": "vertex" + }, + "vertex-partner": { + "className": "VertexExecutor", + "configSource": "vertex", + "provider": "vertex" + }, + "xai": { + "className": "XaiExecutor", + "configSource": "xai", + "provider": "xai" + }, + "xai-oauth": { + "className": "XaiExecutor", + "configSource": "xai-oauth", + "provider": "xai-oauth" + }, + "xao": { + "className": "XaiExecutor", + "configSource": "xai-oauth", + "provider": "xai-oauth" + }, + "ybw": { + "className": "YuanbaoWebExecutor", + "configSource": "", + "provider": "yuanbao-web" + }, + "yuanbao-web": { + "className": "YuanbaoWebExecutor", + "configSource": "", + "provider": "yuanbao-web" + }, + "zai-web": { + "className": "ZaiWebExecutor", + "configSource": "", + "provider": "zai-web" + }, + "zc": { + "className": "ZcodeExecutor", + "configSource": "", + "provider": "zcode" + }, + "zcode": { + "className": "ZcodeExecutor", + "configSource": "", + "provider": "zcode" + }, + "zed-hosted": { + "className": "ZedHostedExecutor", + "configSource": "zed-hosted", + "provider": "zed-hosted" + }, + "zenmux-free": { + "className": "ZenmuxFreeExecutor", + "configSource": "", + "provider": "zenmux-free" + }, + "zmf": { + "className": "ZenmuxFreeExecutor", + "configSource": "", + "provider": "zenmux-free" + }, + "zw": { + "className": "ZaiWebExecutor", + "configSource": "", + "provider": "zai-web" + } + }, + "keyCount": 137, + "sharedInstances": [] +} diff --git a/tests/unit/executor-map-golden.test.ts b/tests/unit/executor-map-golden.test.ts new file mode 100644 index 0000000000..ea83e56756 --- /dev/null +++ b/tests/unit/executor-map-golden.test.ts @@ -0,0 +1,134 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; + +// R0.3 GOLDEN LOCK (characterization BEFORE the ExecutorRegistry refactor): +// freeze the full provider-id → executor mapping of open-sse/executors/index.ts — +// every specialized key with its executor class, effective provider identity and +// which PROVIDERS config entry backs it — plus the getExecutor() dispatch rules +// (specialized hit, DefaultExecutor fallback + cache, cloud-agent guard #6699, +// search-provider guard #10274). The registry refactor must keep this snapshot +// byte-identical: any drift in keys, classes, provider identity or guard behavior +// is a golden diff, not a silent routing change. + +const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-executor-golden-")); +process.env.DATA_DIR = TEST_DATA_DIR; + +// Dynamic imports AFTER DATA_DIR is set so db/core.ts picks up the temp path. +const { getExecutor, hasSpecializedExecutor, DefaultExecutor } = await import( + "../../open-sse/executors/index.ts" +); +const { PROVIDERS } = await import("../../open-sse/config/constants.ts"); +const { SEARCH_PROVIDERS } = await import("../../open-sse/config/searchRegistry.ts"); +const { goldenSnapshot } = await import("../helpers/goldenSnapshot.ts"); + +test.after(() => { + fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true }); +}); + +// The specialized keys are not exported; enumerate them through the public +// surface by probing every plausible id source AND the literal keys read from +// the module source. Reading the source keeps the golden honest: a key added +// to (or removed from) the hard-coded map cannot hide from the snapshot. +function readSpecializedKeys(): string[] { + const src = fs.readFileSync( + path.resolve(path.dirname(new URL(import.meta.url).pathname), "../../open-sse/executors/index.ts"), + "utf8" + ); + const mapMatch = src.match(/const executors = \{([\s\S]*?)\n\};/); + assert.ok(mapMatch, "executors map literal not found in open-sse/executors/index.ts"); + const keys: string[] = []; + for (const line of mapMatch[1].split("\n")) { + const m = line.match(/^\s*(?:"([^"]+)"|([A-Za-z0-9_$-]+)):\s*new /); + if (m) keys.push(m[1] ?? m[2]); + } + return keys; +} + +// Map a ProviderConfig object back to its PROVIDERS key by identity, so the +// snapshot records WHICH config backs each executor without freezing the whole +// (huge, frequently-edited) config content. +const providerConfigKeyByRef = new Map(); +for (const [key, cfg] of Object.entries(PROVIDERS)) { + if (cfg && typeof cfg === "object" && !providerConfigKeyByRef.has(cfg)) { + providerConfigKeyByRef.set(cfg, key); + } +} + +function describeExecutor(instance: unknown): { + className: string; + provider: string | null; + configSource: string | null; +} { + const inst = instance as { constructor: { name: string }; provider?: string; config?: object }; + const cfg = inst.config; + return { + className: inst.constructor.name, + provider: typeof inst.provider === "string" ? inst.provider : null, + configSource: + cfg == null ? null : (providerConfigKeyByRef.get(cfg) ?? ""), + }; +} + +const specializedKeys = readSpecializedKeys(); + +test("golden: specialized executor map — key → class + provider identity + config source", () => { + assert.ok(specializedKeys.length >= 100, `suspiciously few keys: ${specializedKeys.length}`); + + const entries: Record< + string, + { className: string; provider: string | null; configSource: string | null } + > = {}; + const byInstance = new Map(); + + for (const key of [...specializedKeys].sort()) { + assert.equal(hasSpecializedExecutor(key), true, `hasSpecializedExecutor(${key})`); + const instance = getExecutor(key); + entries[key] = describeExecutor(instance); + const group = byInstance.get(instance) ?? []; + group.push(key); + byInstance.set(instance, group); + } + + // Keys sharing the SAME instance share per-instance state (session pools, + // rotation cooldowns); today every map entry is its own `new X()`. Freeze that. + const sharedInstances = [...byInstance.values()] + .filter((keys) => keys.length > 1) + .map((keys) => keys.sort()) + .sort((a, b) => a[0].localeCompare(b[0])); + + goldenSnapshot("executors/executor-map", { + keyCount: specializedKeys.length, + entries, + sharedInstances, + }); +}); + +test("golden: getExecutor dispatch rules — fallback, cache and 400-guards", () => { + // 1. Unknown provider → DefaultExecutor for that provider, memoized. + const unknown = "golden-test-unknown-provider"; + assert.equal(hasSpecializedExecutor(unknown), false); + const fallback = getExecutor(unknown); + assert.ok(fallback instanceof DefaultExecutor, "fallback must be DefaultExecutor"); + assert.equal(getExecutor(unknown), fallback, "DefaultExecutor fallback must be cached"); + + // 2. Cloud-agent guard (#6699) and search guard (#10274) → status-400 throw. + const guardOutcome = (provider: string) => { + try { + getExecutor(provider); + return { throws: false as const }; + } catch (err) { + const e = err as Error & { status?: number }; + return { throws: true as const, status: e.status ?? null, message: e.message }; + } + }; + + const searchProviders = Object.keys(SEARCH_PROVIDERS).sort(); + goldenSnapshot("executors/dispatch-rules", { + fallback: describeExecutor(fallback), + cloudAgentGuard: { jules: guardOutcome("jules") }, + searchGuard: Object.fromEntries(searchProviders.map((p) => [p, guardOutcome(p)])), + }); +});