From 728603d788deb55507dc0b6106f75860073037f1 Mon Sep 17 00:00:00 2001 From: 777genius Date: Tue, 21 Apr 2026 18:06:35 +0300 Subject: [PATCH] test(teams): stabilize runtime launch validation mocks --- ...TeamProvisioningServicePostCompact.test.ts | 53 ++++++++++++++++++- .../services/team/TeammateToolTracker.test.ts | 4 +- .../utils/teamModelAvailability.test.ts | 40 ++++++++++++-- 3 files changed, 90 insertions(+), 7 deletions(-) diff --git a/test/main/services/team/TeamProvisioningServicePostCompact.test.ts b/test/main/services/team/TeamProvisioningServicePostCompact.test.ts index 4ce12284..ff2d20c2 100644 --- a/test/main/services/team/TeamProvisioningServicePostCompact.test.ts +++ b/test/main/services/team/TeamProvisioningServicePostCompact.test.ts @@ -23,6 +23,7 @@ vi.mock('@main/services/team/ClaudeBinaryResolver', () => ({ vi.mock('@main/utils/childProcess', () => ({ spawnCli: vi.fn(), + execCli: vi.fn(), killProcessTree: vi.fn(), })); @@ -39,7 +40,7 @@ vi.mock('@main/utils/pathDecoder', async (importOriginal) => { import { TeamProvisioningService } from '@main/services/team/TeamProvisioningService'; import { ClaudeBinaryResolver } from '@main/services/team/ClaudeBinaryResolver'; -import { spawnCli } from '@main/utils/childProcess'; +import { execCli, spawnCli } from '@main/utils/childProcess'; import { setAppDataBasePath } from '@main/utils/pathDecoder'; function createFakeChild() { @@ -78,6 +79,56 @@ async function setupRunningTeam(teamName: string) { vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/fake/claude'); const { child, writeSpy } = createFakeChild(); vi.mocked(spawnCli).mockReturnValue(child as any); + vi.mocked(execCli).mockImplementation(async (_binaryPath, args) => { + const providerIndex = args.indexOf('--provider'); + const providerId = providerIndex >= 0 ? args[providerIndex + 1] : 'anthropic'; + if (args[0] === 'model' && args[1] === 'list') { + return { + stdout: JSON.stringify({ + providers: { + [providerId ?? 'anthropic']: { + defaultModel: providerId === 'codex' ? 'gpt-5.4' : 'opus[1m]', + models: + providerId === 'codex' + ? ['gpt-5.4'] + : ['opus[1m]', 'opus', 'claude-opus-4-6', 'sonnet', 'haiku'], + }, + }, + }), + stderr: '', + }; + } + if (args[0] === 'runtime' && args[1] === 'status') { + return { + stdout: JSON.stringify({ + providers: { + [providerId ?? 'anthropic']: { + runtimeCapabilities: + providerId === 'codex' + ? { + reasoningEffort: { + supported: true, + values: ['minimal', 'low', 'medium', 'high', 'xhigh'], + configPassthrough: true, + }, + } + : { + fastMode: { + supported: false, + available: false, + reason: 'Test runtime does not expose fast mode.', + source: 'test', + }, + }, + modelCatalog: null, + }, + }, + }), + stderr: '', + }; + } + return { stdout: '{}', stderr: '' }; + }); const svc = new TeamProvisioningService(); (svc as any).buildProvisioningEnv = vi.fn(async () => ({ diff --git a/test/main/services/team/TeammateToolTracker.test.ts b/test/main/services/team/TeammateToolTracker.test.ts index a41e0ccc..acc9a25f 100644 --- a/test/main/services/team/TeammateToolTracker.test.ts +++ b/test/main/services/team/TeammateToolTracker.test.ts @@ -25,7 +25,7 @@ async function createSubagentLog( return filePath; } -async function waitForCondition(check: () => void, attempts = 20): Promise { +async function waitForCondition(check: () => void, attempts = 100): Promise { let lastError: unknown; for (let attempt = 0; attempt < attempts; attempt += 1) { try { @@ -161,6 +161,8 @@ describe('TeammateToolTracker', () => { tracker.handleLogSourceChange('my-team'); await waitForCondition(() => { expect(events).toHaveLength(1); + const fileState = (tracker as any).stateByTeam.get('my-team')?.filesByPath.get(filePath); + expect(fileState?.lineCarry).toBe(resultLine.slice(0, splitAt).trim()); }); await appendFile(filePath, `${resultLine.slice(splitAt)}\n`, 'utf8'); diff --git a/test/renderer/utils/teamModelAvailability.test.ts b/test/renderer/utils/teamModelAvailability.test.ts index 2c9b03b1..134a3d18 100644 --- a/test/renderer/utils/teamModelAvailability.test.ts +++ b/test/renderer/utils/teamModelAvailability.test.ts @@ -140,11 +140,41 @@ describe('teamModelAvailability', () => { it('keeps both Anthropic Opus 4.7 and explicit Opus 4.6 in the fallback selector options', () => { expect(getAvailableTeamProviderModelOptions('anthropic')).toEqual([ - { value: '', label: 'Default', badgeLabel: 'Default' }, - { value: 'opus', label: 'Opus 4.7', badgeLabel: 'Opus 4.7' }, - { value: 'claude-opus-4-6', label: 'Opus 4.6', badgeLabel: 'Opus 4.6' }, - { value: 'sonnet', label: 'Sonnet 4.6', badgeLabel: 'Sonnet 4.6' }, - { value: 'haiku', label: 'Haiku 4.5', badgeLabel: 'Haiku 4.5' }, + { + value: '', + label: 'Default', + badgeLabel: 'Default', + availabilityStatus: undefined, + availabilityReason: undefined, + }, + { + value: 'opus', + label: 'Opus 4.7', + badgeLabel: 'Opus 4.7', + availabilityStatus: 'available', + availabilityReason: null, + }, + { + value: 'claude-opus-4-6', + label: 'Opus 4.6', + badgeLabel: 'Opus 4.6', + availabilityStatus: 'available', + availabilityReason: null, + }, + { + value: 'sonnet', + label: 'Sonnet 4.6', + badgeLabel: 'Sonnet 4.6', + availabilityStatus: 'available', + availabilityReason: null, + }, + { + value: 'haiku', + label: 'Haiku 4.5', + badgeLabel: 'Haiku 4.5', + availabilityStatus: 'available', + availabilityReason: null, + }, ]); });