import { describe, expect, it, vi } from 'vitest'; import { OpenCodeReadinessBridge, type OpenCodeReadinessBridgeCommandExecutor, type OpenCodeProductionE2EEvidenceReadPort, } from '../../../../src/main/services/team/opencode/bridge/OpenCodeReadinessBridge'; import { OPENCODE_PRODUCTION_E2E_READY_CHECKPOINTS, OPENCODE_PRODUCTION_E2E_REQUIRED_SIGNALS, buildOpenCodeProjectPathFingerprint, type OpenCodeProductionE2EEvidence, } from '../../../../src/main/services/team/opencode/e2e/OpenCodeProductionE2EEvidence'; import { buildOpenCodeCanonicalMcpToolId, REQUIRED_AGENT_TEAMS_RUNTIME_TOOLS, } from '../../../../src/main/services/team/opencode/mcp/OpenCodeMcpToolAvailability'; import type { OpenCodeTeamLaunchReadiness } from '../../../../src/main/services/team/opencode/readiness/OpenCodeTeamLaunchReadiness'; import type { OpenCodeBridgeFailureKind, OpenCodeBridgeCommandName, OpenCodeBridgeResult, OpenCodeBridgeSuccess, OpenCodeLaunchTeamCommandData, } from '../../../../src/main/services/team/opencode/bridge/OpenCodeBridgeCommandContract'; describe('OpenCodeReadinessBridge', () => { it('executes the read-only opencode.readiness command and returns readiness data', async () => { const readinessResult = readiness({ state: 'ready', launchAllowed: true }); const executor = fakeExecutor(bridgeSuccess(readinessResult)); const bridge = new OpenCodeReadinessBridge(executor, { timeoutMs: 15_000 }); await expect( bridge.checkOpenCodeTeamLaunchReadiness({ projectPath: '/repo', selectedModel: 'openai/gpt-5.4-mini', requireExecutionProbe: true, }) ).resolves.toBe(readinessResult); expect(executor.execute).toHaveBeenCalledWith( 'opencode.readiness', { projectPath: '/repo', selectedModel: 'openai/gpt-5.4-mini', requireExecutionProbe: true, }, { cwd: '/repo', timeoutMs: 15_000, } ); expect(bridge.getLastOpenCodeRuntimeSnapshot('/repo')).toMatchObject({ capabilitySnapshotId: 'cap-1', version: '1.14.19', }); }); it('maps bridge failures into fail-closed readiness', async () => { const executor = fakeExecutor( bridgeFailure('timeout', 'OpenCode readiness command timed out', [ { id: 'diag-1', type: 'opencode_bridge_unknown_outcome', providerId: 'opencode', severity: 'warning', message: 'timed out', createdAt: '2026-04-21T12:00:00.000Z', }, ]) ); const bridge = new OpenCodeReadinessBridge(executor); await expect( bridge.checkOpenCodeTeamLaunchReadiness({ projectPath: '/repo', selectedModel: 'openai/gpt-5.4-mini', requireExecutionProbe: false, }) ).resolves.toMatchObject({ state: 'unknown_error', launchAllowed: false, modelId: 'openai/gpt-5.4-mini', hostHealthy: false, requiredToolsPresent: false, missing: ['OpenCode readiness command timed out'], diagnostics: [ 'OpenCode readiness bridge failed: timeout: OpenCode readiness command timed out', 'opencode_bridge_unknown_outcome: timed out', ], }); expect(bridge.getLastOpenCodeRuntimeSnapshot('/repo')).toBeNull(); }); it('blocks production readiness when strict production E2E evidence is missing', async () => { const executor = fakeExecutor( bridgeSuccess(readiness({ state: 'ready', launchAllowed: true })) ); const evidence = fakeEvidenceStore(null); const bridge = new OpenCodeReadinessBridge(executor, { productionE2eEvidence: evidence }); await expect( bridge.checkOpenCodeTeamLaunchReadiness({ projectPath: '/repo', selectedModel: 'openai/gpt-5.4-mini', requireExecutionProbe: true, launchMode: 'production', }) ).resolves.toMatchObject({ state: 'e2e_missing', launchAllowed: false, supportLevel: 'supported_e2e_pending', missing: ['OpenCode production launch requires a current production E2E evidence artifact'], diagnostics: [ 'OpenCode production launch requires a current production E2E evidence artifact', ], }); expect(evidence.read).toHaveBeenCalledOnce(); }); it('allows dogfood readiness while surfacing missing production E2E evidence diagnostics', async () => { const executor = fakeExecutor( bridgeSuccess(readiness({ state: 'ready', launchAllowed: true })) ); const bridge = new OpenCodeReadinessBridge(executor, { productionE2eEvidence: fakeEvidenceStore(null), }); await expect( bridge.checkOpenCodeTeamLaunchReadiness({ projectPath: '/repo', selectedModel: 'openai/gpt-5.4-mini', requireExecutionProbe: true, launchMode: 'dogfood', }) ).resolves.toMatchObject({ state: 'ready', launchAllowed: true, supportLevel: 'supported_e2e_pending', diagnostics: [ 'OpenCode production launch requires a current production E2E evidence artifact', ], }); }); it('keeps production readiness open when evidence matches runtime identity and project context', async () => { const executor = fakeExecutor( bridgeSuccess(readiness({ state: 'ready', launchAllowed: true })) ); const evidence = fakeEvidenceStore(productionEvidence()); const bridge = new OpenCodeReadinessBridge(executor, { productionE2eEvidence: evidence, }); await expect( bridge.checkOpenCodeTeamLaunchReadiness({ projectPath: '/repo', selectedModel: 'openai/gpt-5.4-mini', requireExecutionProbe: true, launchMode: 'production', }) ).resolves.toMatchObject({ state: 'ready', launchAllowed: true, supportLevel: 'production_supported', diagnostics: [], }); expect(evidence.read).toHaveBeenCalledWith({ selectedModel: 'openai/gpt-5.4-mini', projectPathFingerprint: buildOpenCodeProjectPathFingerprint('/repo'), opencodeVersion: '1.14.19', binaryFingerprint: 'bin-1', capabilitySnapshotId: 'cap-1', }); }); it('accepts production evidence recorded with a different OpenCode model when runtime identity matches', async () => { const executor = fakeExecutor( bridgeSuccess(readiness({ state: 'ready', launchAllowed: true })) ); const evidence = fakeEvidenceStore( productionEvidence({ selectedModel: 'opencode/minimax-m2.5-free' }) ); const bridge = new OpenCodeReadinessBridge(executor, { productionE2eEvidence: evidence, }); await expect( bridge.checkOpenCodeTeamLaunchReadiness({ projectPath: '/repo', selectedModel: 'opencode/nemotron-3-super-free', requireExecutionProbe: true, launchMode: 'production', }) ).resolves.toMatchObject({ state: 'ready', launchAllowed: true, supportLevel: 'production_supported', diagnostics: [], }); }); it('routes state-changing launch commands through the guarded command service when configured', async () => { const executor = fakeExecutor( bridgeFailure('internal_error', 'direct bridge must not run', []) ); const stateChangingExecute = vi.fn(); const stateChangingCommands = { async execute
(input: { command: OpenCodeBridgeCommandName; body: TBody; }): Promise