From 656480546b6818bd8fd804c60c72e9b6541c9612 Mon Sep 17 00:00:00 2001 From: Xubin Ren <52506698+Re-bin@users.noreply.github.com> Date: Sat, 15 Aug 2026 17:22:08 +0800 Subject: [PATCH] test(providers): cover OrcaRouter WebUI integration --- tests/webui/test_settings_api.py | 57 ++++++++++++++++ webui/src/tests/settings-models.test.tsx | 84 ++++++++++++++++++++++++ 2 files changed, 141 insertions(+) diff --git a/tests/webui/test_settings_api.py b/tests/webui/test_settings_api.py index 8b3615222..1ec93d361 100644 --- a/tests/webui/test_settings_api.py +++ b/tests/webui/test_settings_api.py @@ -111,6 +111,26 @@ def test_settings_payload_exposes_edenai_provider( assert edenai["model_selectable"] is True +def test_settings_payload_exposes_orcarouter_provider( + tmp_path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + config_path = tmp_path / "config.json" + config = Config() + config.providers.orcarouter.api_key = "sk-orca-test" + save_config(config, config_path) + monkeypatch.setattr("nanobot.config.loader._current_config_path", config_path) + + payload = settings_payload() + orcarouter = next(row for row in payload["providers"] if row["name"] == "orcarouter") + + assert orcarouter["label"] == "OrcaRouter" + assert orcarouter["configured"] is True + assert orcarouter["default_api_base"] == "https://api.orcarouter.ai/v1" + assert orcarouter["model_catalog"] == "catalog" + assert orcarouter["model_selectable"] is True + + def test_settings_payload_includes_relocated_capabilities( tmp_path, monkeypatch: pytest.MonkeyPatch, @@ -1958,10 +1978,47 @@ def test_provider_models_payload_requires_gateway_key( assert payload["models"] == [] +def test_provider_models_payload_fetches_orcarouter_catalog( + tmp_path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + config_path = tmp_path / "config.json" + config = Config() + config.providers.orcarouter.api_key = "sk-orca-test" + save_config(config, config_path) + monkeypatch.setattr("nanobot.config.loader._current_config_path", config_path) + + def fake_get(url: str, **kwargs): + assert url == "https://api.orcarouter.ai/v1/models" + assert kwargs["headers"]["Authorization"] == "Bearer sk-orca-test" + return httpx.Response( + 200, + json={ + "data": [ + {"id": "orcarouter/auto", "owned_by": "orcarouter"}, + {"id": "anthropic/claude-sonnet-4.6", "owned_by": "anthropic"}, + ] + }, + request=httpx.Request("GET", url), + ) + + monkeypatch.setattr("nanobot.webui.settings_api.httpx.get", fake_get) + + payload = provider_models_payload({"provider": ["orcarouter"]}) + + assert payload["status"] == "available" + assert payload["catalog_kind"] == "catalog" + assert [model["id"] for model in payload["models"]] == [ + "orcarouter/auto", + "anthropic/claude-sonnet-4.6", + ] + + def test_model_catalog_kind_uses_provider_spec_metadata() -> None: assert _model_catalog_kind(find_by_name("skywork")) == "official" assert _model_catalog_kind(find_by_name("anthropic")) == "unsupported" assert _model_catalog_kind(find_by_name("openrouter")) == "catalog" + assert _model_catalog_kind(find_by_name("orcarouter")) == "catalog" assert _model_catalog_kind(find_by_name("openai_codex")) == "builtin" diff --git a/webui/src/tests/settings-models.test.tsx b/webui/src/tests/settings-models.test.tsx index 6032afc06..e5fd4417b 100644 --- a/webui/src/tests/settings-models.test.tsx +++ b/webui/src/tests/settings-models.test.tsx @@ -1003,6 +1003,90 @@ describe("Settings models", () => { ).toBe(false); }); + it("defers the OrcaRouter catalog until the user searches", async () => { + const base = settingsPayload(); + const payload: SettingsPayload = { + ...base, + agent: { + ...base.agent, + model: "orcarouter/auto", + provider: "orcarouter", + resolved_provider: "orcarouter", + }, + model_presets: [ + { + ...base.model_presets[0], + model: "orcarouter/auto", + provider: "orcarouter", + resolved_provider: "orcarouter", + }, + ], + providers: [ + { + name: "orcarouter", + label: "OrcaRouter", + configured: true, + auth_type: "api_key", + api_key_required: true, + api_key_hint: "sk-o••••test", + api_base: null, + default_api_base: "https://api.orcarouter.ai/v1", + model_catalog: "catalog", + }, + ], + }; + const fetchMock = vi.fn(async (input: RequestInfo | URL) => { + const url = String(input); + if (url === "/api/settings") return jsonResponse(payload); + if (url === "/api/settings/cli-apps") { + return jsonResponse({ apps: [], installed_count: 0 }); + } + if (url === "/api/settings/mcp-presets") { + return jsonResponse({ presets: [], installed_count: 0 }); + } + if (url === "/api/settings/provider-models?provider=orcarouter") { + return jsonResponse({ + provider: "orcarouter", + label: "OrcaRouter", + status: "available", + catalog_kind: "catalog", + models: [ + { id: "orcarouter/auto", owned_by: "orcarouter" }, + { id: "anthropic/claude-sonnet-4.6", owned_by: "anthropic" }, + ], + model_count: 2, + fetched_at: 1, + }); + } + return { ok: false, status: 404, json: async () => ({}) } as Response; + }); + vi.stubGlobal("fetch", fetchMock); + + renderSettingsView({ initialSection: "models" }); + + await togglePresetEditor(); + const modelButtons = await screen.findAllByRole("button", { name: /orcarouter\/auto/i }); + await openPopover(modelButtons[modelButtons.length - 1]); + expect(await screen.findByText("Search provider catalog to choose a model.")).toBeInTheDocument(); + expect( + fetchMock.mock.calls.some(([input]) => + String(input).startsWith("/api/settings/provider-models"), + ), + ).toBe(false); + + fireEvent.change(screen.getByPlaceholderText("Search or type model ID"), { + target: { value: "cl" }, + }); + + await screen.findByText("anthropic/claude-sonnet-4.6"); + expect(fetchMock).toHaveBeenCalledWith( + "/api/settings/provider-models?provider=orcarouter", + expect.objectContaining({ + headers: { Authorization: "Bearer tok" }, + }), + ); + }); + it("loads curated models for configured OAuth providers", async () => { const base = settingsPayload(); const payload: SettingsPayload = {