From d8fe1c0b4195a8743cd8121a05d9f147c4e60c6d Mon Sep 17 00:00:00 2001 From: infinitycrew39 Date: Wed, 24 Jun 2026 23:19:51 +0700 Subject: [PATCH] test(desktop): cover scoped onboarding runtime readiness checks Assert setup.runtime_check honors provider params and that Nous OAuth onboarding persists model config before validating the connected provider. --- .../desktop/src/lib/runtime-readiness.test.ts | 50 ++++++++++++++++++- apps/desktop/src/store/onboarding.test.ts | 12 ++++- tests/test_tui_gateway_server.py | 33 ++++++++++++ 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/lib/runtime-readiness.test.ts b/apps/desktop/src/lib/runtime-readiness.test.ts index 54a25828c7e..83a1d2a2bdd 100644 --- a/apps/desktop/src/lib/runtime-readiness.test.ts +++ b/apps/desktop/src/lib/runtime-readiness.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' -import { interpretRuntimeReadiness } from './runtime-readiness' +import { evaluateRuntimeReadiness, fetchRuntimeReadinessSignals, interpretRuntimeReadiness } from './runtime-readiness' describe('interpretRuntimeReadiness', () => { it('prefers runtime_check when both signals exist', () => { @@ -63,3 +63,51 @@ describe('interpretRuntimeReadiness', () => { expect(result.reason).toBe('setup.runtime_check timeout') }) }) + +describe('fetchRuntimeReadinessSignals', () => { + it('scopes setup.runtime_check to the requested provider', async () => { + const calls: Array<{ method: string; params?: Record }> = [] + const requestGateway = async (method: string, params?: Record) => { + calls.push({ method, params }) + + if (method === 'setup.status') { + return { provider_configured: true } as T + } + + if (method === 'setup.runtime_check') { + return { ok: true } as T + } + + throw new Error(`unexpected method: ${method}`) + } + + await fetchRuntimeReadinessSignals(requestGateway, 'nous') + + expect(calls).toEqual([ + { method: 'setup.status' }, + { method: 'setup.runtime_check', params: { provider: 'nous' } } + ]) + }) +}) + +describe('evaluateRuntimeReadiness', () => { + it('forwards requestedProvider to setup.runtime_check', async () => { + const requestGateway = async (method: string, params?: Record) => { + if (method === 'setup.status') { + return { provider_configured: true } as T + } + + if (method === 'setup.runtime_check') { + expect(params).toEqual({ provider: 'nous' }) + + return { ok: true } as T + } + + throw new Error(`unexpected method: ${method}`) + } + + const result = await evaluateRuntimeReadiness(requestGateway, { requestedProvider: 'nous' }) + + expect(result.ready).toBe(true) + }) +}) diff --git a/apps/desktop/src/store/onboarding.test.ts b/apps/desktop/src/store/onboarding.test.ts index 7173e89f572..10c3159f021 100644 --- a/apps/desktop/src/store/onboarding.test.ts +++ b/apps/desktop/src/store/onboarding.test.ts @@ -194,7 +194,7 @@ describe('OAuth onboarding', () => { throw new Error(`unexpected api path: ${path}`) }) - const requestGateway: OnboardingContext['requestGateway'] = async method => { + const requestGateway: OnboardingContext['requestGateway'] = async (method, params) => { if (method === 'reload.env') { return {} as never } @@ -204,6 +204,8 @@ describe('OAuth onboarding', () => { } if (method === 'setup.runtime_check') { + expect(params).toEqual({ provider: 'nous' }) + return { ok: true } as never } @@ -241,6 +243,14 @@ describe('OAuth onboarding', () => { } expect(calls.some(c => c.path === '/api/model/set')).toBe(true) + + const optionsIndex = calls.findIndex(c => c.path === '/api/model/options') + const recommendedIndex = calls.findIndex(c => c.path.startsWith('/api/model/recommended-default')) + const setIndex = calls.findIndex(c => c.path === '/api/model/set') + + expect(optionsIndex).toBeGreaterThanOrEqual(0) + expect(recommendedIndex).toBeGreaterThan(optionsIndex) + expect(setIndex).toBeGreaterThan(recommendedIndex) }) }) diff --git a/tests/test_tui_gateway_server.py b/tests/test_tui_gateway_server.py index 8d7dce2bd79..e88128a3c97 100644 --- a/tests/test_tui_gateway_server.py +++ b/tests/test_tui_gateway_server.py @@ -2784,6 +2784,39 @@ def test_setup_runtime_check_rejects_implicit_bedrock_when_unconfigured(monkeypa assert resp["result"]["provider"] == "bedrock" +def test_setup_runtime_check_honors_requested_provider(monkeypatch): + """Onboarding must be able to validate the provider the user just connected.""" + monkeypatch.setattr("hermes_cli.main._has_any_provider_configured", lambda: True) + + def fake_resolve(requested=None, **kwargs): + if requested == "nous": + return { + "provider": "nous", + "api_key": "invoke-jwt", + "source": "portal", + } + return { + "provider": "anthropic", + "api_key": "", + "source": "config", + } + + monkeypatch.setattr( + "hermes_cli.runtime_provider.resolve_runtime_provider", + fake_resolve, + ) + + scoped = server.handle_request( + {"id": "1", "method": "setup.runtime_check", "params": {"provider": "nous"}} + ) + assert scoped["result"]["ok"] is True + assert scoped["result"]["provider"] == "nous" + + default = server.handle_request({"id": "1", "method": "setup.runtime_check", "params": {}}) + assert default["result"]["ok"] is False + assert default["result"]["provider"] == "anthropic" + + def test_complete_slash_drops_removed_provider_alias(): # `/provider` was folded into a single `/model` command, so autocomplete # must no longer offer the dead alias...