diff --git a/packages/vscode-ide-companion/src/services/qwenConnectionHandler.test.ts b/packages/vscode-ide-companion/src/services/qwenConnectionHandler.test.ts new file mode 100644 index 0000000000..21f2f4b0f4 --- /dev/null +++ b/packages/vscode-ide-companion/src/services/qwenConnectionHandler.test.ts @@ -0,0 +1,136 @@ +/** + * @license + * Copyright 2025 Qwen Team + * SPDX-License-Identifier: Apache-2.0 + */ + +import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; + +vi.mock('vscode', () => ({ + workspace: { + getConfiguration: vi.fn(), + }, +})); + +import { QwenConnectionHandler } from './qwenConnectionHandler.js'; +import type { AcpConnection } from './acpConnection.js'; + +describe('QwenConnectionHandler', () => { + let handler: QwenConnectionHandler; + let mockConnection: AcpConnection; + let mockGetConfiguration: ReturnType; + + beforeEach(async () => { + const vscode = await import('vscode'); + mockGetConfiguration = vscode.workspace.getConfiguration as ReturnType< + typeof vi.fn + >; + mockGetConfiguration.mockReset(); + + handler = new QwenConnectionHandler(); + mockConnection = { + connect: vi.fn().mockResolvedValue(undefined), + newSession: vi.fn().mockResolvedValue({ sessionId: 'test-session' }), + authenticate: vi.fn().mockResolvedValue({}), + } as unknown as AcpConnection; + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + describe('proxy configuration', () => { + it('passes --proxy argument when http.proxy is set', async () => { + mockGetConfiguration.mockReturnValue({ + get: (key: string) => { + if (key === 'proxy') { + return 'http://proxy.example.com:8080'; + } + return undefined; + }, + }); + + await handler.connect(mockConnection, '/workspace', '/path/to/cli.js'); + + expect(mockConnection.connect).toHaveBeenCalled(); + const connectArgs = (mockConnection.connect as ReturnType) + .mock.calls[0]; + expect(connectArgs[2]).toContain('--proxy'); + expect(connectArgs[2]).toContain('http://proxy.example.com:8080'); + }); + + it('passes --proxy argument when https.proxy is set (fallback)', async () => { + mockGetConfiguration.mockReturnValue({ + get: (key: string) => { + if (key === 'proxy') { + return undefined; + } + if (key === 'https.proxy') { + return 'http://https-proxy.example.com:8080'; + } + return undefined; + }, + }); + + await handler.connect(mockConnection, '/workspace', '/path/to/cli.js'); + + expect(mockConnection.connect).toHaveBeenCalled(); + const connectArgs = (mockConnection.connect as ReturnType) + .mock.calls[0]; + expect(connectArgs[2]).toContain('--proxy'); + expect(connectArgs[2]).toContain('http://https-proxy.example.com:8080'); + }); + + it('prefers http.proxy over https.proxy', async () => { + mockGetConfiguration.mockReturnValue({ + get: (key: string) => { + if (key === 'proxy') { + return 'http://http-proxy.example.com:8080'; + } + if (key === 'https.proxy') { + return 'http://https-proxy.example.com:8080'; + } + return undefined; + }, + }); + + await handler.connect(mockConnection, '/workspace', '/path/to/cli.js'); + + const connectArgs = (mockConnection.connect as ReturnType) + .mock.calls[0]; + expect(connectArgs[2]).toContain('http://http-proxy.example.com:8080'); + expect(connectArgs[2]).not.toContain( + 'http://https-proxy.example.com:8080', + ); + }); + + it('does not pass --proxy argument when no proxy is configured', async () => { + mockGetConfiguration.mockReturnValue({ + get: () => undefined, + }); + + await handler.connect(mockConnection, '/workspace', '/path/to/cli.js'); + + const connectArgs = (mockConnection.connect as ReturnType) + .mock.calls[0]; + expect(connectArgs[2]).not.toContain('--proxy'); + }); + + it('does not pass --proxy argument when proxy is empty string', async () => { + mockGetConfiguration.mockReturnValue({ + get: (key: string) => { + if (key === 'proxy') { + return ''; + } + return undefined; + }, + }); + + await handler.connect(mockConnection, '/workspace', '/path/to/cli.js'); + + const connectArgs = (mockConnection.connect as ReturnType) + .mock.calls[0]; + expect(connectArgs[2]).not.toContain('--proxy'); + }); + }); +}); diff --git a/packages/vscode-ide-companion/src/services/qwenConnectionHandler.ts b/packages/vscode-ide-companion/src/services/qwenConnectionHandler.ts index 60c0b3ac57..6ba990317f 100644 --- a/packages/vscode-ide-companion/src/services/qwenConnectionHandler.ts +++ b/packages/vscode-ide-companion/src/services/qwenConnectionHandler.ts @@ -10,6 +10,7 @@ * Handles Qwen Agent connection establishment, authentication, and session creation */ +import * as vscode from 'vscode'; import type { AcpConnection } from './acpConnection.js'; import { isAuthenticationRequiredError } from '../utils/authErrors.js'; import { authMethod } from '../types/acpTypes.js'; @@ -73,6 +74,16 @@ export class QwenConnectionHandler { // Build extra CLI arguments (only essential parameters) const extraArgs: string[] = []; + const httpConfig = vscode.workspace.getConfiguration('http'); + const proxyUrl = + httpConfig.get('proxy') || httpConfig.get('https.proxy'); + if (proxyUrl) { + extraArgs.push('--proxy', proxyUrl); + console.log( + '[QwenAgentManager] Using proxy from VSCode settings:', + proxyUrl, + ); + } await connection.connect(cliEntryPath!, workingDir, extraArgs);