test(runtime-provider-management): cover project-aware opencode flow

This commit is contained in:
777genius 2026-04-25 18:40:01 +03:00
parent 2eba377be2
commit 825cfc00d1
3 changed files with 98 additions and 1 deletions

View file

@ -3,6 +3,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
const buildProviderAwareCliEnvMock = vi.fn();
const resolveBinaryMock = vi.fn();
const execCliMock = vi.fn();
const spawnCliMock = vi.fn();
const resolveInteractiveShellEnvMock = vi.fn();
vi.mock('@main/services/runtime/providerAwareCliEnv', () => ({
@ -17,7 +18,7 @@ vi.mock('@main/services/team/ClaudeBinaryResolver', () => ({
vi.mock('@main/utils/childProcess', () => ({
execCli: (...args: unknown[]) => execCliMock(...args),
spawnCli: vi.fn(),
spawnCli: (...args: unknown[]) => spawnCliMock(...args),
killProcessTree: vi.fn(),
}));
@ -86,4 +87,41 @@ describe('AgentTeamsRuntimeProviderManagementCliClient', () => {
'Provider opencode must be connected before testing a model'
);
});
it('passes project path as cwd and CLI flag for project-aware provider management', async () => {
execCliMock.mockResolvedValue({
stdout: JSON.stringify({
schemaVersion: 1,
runtimeId: 'opencode',
view: {
runtimeId: 'opencode',
title: 'OpenCode',
runtime: {
state: 'ready',
cliPath: '/opt/homebrew/bin/opencode',
version: '1.0.0',
managedProfile: 'active',
localAuth: 'synced',
},
providers: [],
defaultModel: null,
fallbackModel: null,
diagnostics: [],
},
}),
stderr: '',
});
const client = new AgentTeamsRuntimeProviderManagementCliClient();
await client.loadView({
runtimeId: 'opencode',
projectPath: '/Users/test/project',
});
expect(execCliMock).toHaveBeenCalledWith(
'/repo/cli-dev',
expect.arrayContaining(['--project-path', '/Users/test/project']),
expect.objectContaining({ cwd: '/Users/test/project' })
);
});
});

View file

@ -67,10 +67,12 @@ vi.mock('@features/runtime-provider-management/renderer', () => ({
runtimeId,
open,
disabled,
projectPath,
}: {
runtimeId: string;
open: boolean;
disabled?: boolean;
projectPath?: string | null;
}) =>
React.createElement(
'section',
@ -79,6 +81,7 @@ vi.mock('@features/runtime-provider-management/renderer', () => ({
'data-runtime-id': runtimeId,
'data-open': String(open),
'data-disabled': String(Boolean(disabled)),
'data-project-path': projectPath ?? '',
},
`Runtime provider management: ${runtimeId}`
),
@ -1453,6 +1456,7 @@ describe('ProviderRuntimeSettingsDialog', () => {
onOpenChange: vi.fn(),
providers: [createOpenCodeProvider()],
initialProviderId: 'opencode',
projectPath: '/tmp/project-a',
onSelectBackend: vi.fn(),
onRefreshProvider: vi.fn(() => Promise.resolve(undefined)),
})
@ -1464,6 +1468,7 @@ describe('ProviderRuntimeSettingsDialog', () => {
expect(panel).not.toBeNull();
expect(panel?.getAttribute('data-runtime-id')).toBe('opencode');
expect(panel?.getAttribute('data-open')).toBe('true');
expect(panel?.getAttribute('data-project-path')).toBe('/tmp/project-a');
expect(host.textContent).toContain('Runtime provider management: opencode');
expect(host.textContent).not.toContain('Desktop currently exposes status only.');
});

View file

@ -41,6 +41,17 @@ describe('useRuntimeProviderManagement', () => {
return React.createElement('div');
}
function EnabledHarness(props: { projectPath?: string | null }): React.ReactElement {
const hook = useRuntimeProviderManagement({
runtimeId: 'opencode',
enabled: true,
projectPath: props.projectPath,
});
state = hook[0];
actions = hook[1];
return React.createElement('div');
}
beforeEach(() => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
host = document.createElement('div');
@ -74,6 +85,49 @@ describe('useRuntimeProviderManagement', () => {
expect(getStoredCreateTeamModel('opencode')).toBe(modelId);
});
it('passes projectPath to the runtime provider management API', async () => {
const loadView = vi.fn(() =>
Promise.resolve({
schemaVersion: 1,
runtimeId: 'opencode',
view: {
runtimeId: 'opencode',
title: 'OpenCode',
runtime: {
state: 'ready',
cliPath: '/opt/homebrew/bin/opencode',
version: '1.0.0',
managedProfile: 'active',
localAuth: 'synced',
},
providers: [],
defaultModel: null,
fallbackModel: null,
diagnostics: [],
},
})
);
Object.defineProperty(window, 'electronAPI', {
configurable: true,
value: {
runtimeProviderManagement: {
loadView,
},
} as unknown as ElectronAPI,
});
const root = createRoot(host);
await act(async () => {
root.render(React.createElement(EnabledHarness, { projectPath: '/tmp/project-a' }));
await Promise.resolve();
});
expect(loadView).toHaveBeenCalledWith({
runtimeId: 'opencode',
projectPath: '/tmp/project-a',
});
});
it('keeps failed model probes scoped to the model result instead of a global success banner', async () => {
const modelId = 'openrouter/anthropic/claude-3.5-haiku';
const message =