mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-29 12:11:09 +00:00
337 lines
10 KiB
TypeScript
337 lines
10 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Qwen
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
import {
|
|
getSystemInfo,
|
|
getExtendedSystemInfo,
|
|
getNpmVersion,
|
|
getSandboxEnv,
|
|
getIdeClientName,
|
|
} from './systemInfo.js';
|
|
import type { CommandContext } from '../ui/commands/types.js';
|
|
import { createMockCommandContext } from '../test-utils/mockCommandContext.js';
|
|
import * as child_process from 'node:child_process';
|
|
import os from 'node:os';
|
|
import { IdeClient } from '@qwen-code/qwen-code-core';
|
|
import * as versionUtils from './version.js';
|
|
import type { ExecSyncOptions } from 'node:child_process';
|
|
|
|
vi.mock('node:child_process');
|
|
|
|
vi.mock('node:os', () => ({
|
|
default: {
|
|
release: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
vi.mock('./version.js', () => ({
|
|
getCliVersion: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('@qwen-code/qwen-code-core', async (importOriginal) => {
|
|
const actual =
|
|
await importOriginal<typeof import('@qwen-code/qwen-code-core')>();
|
|
return {
|
|
...actual,
|
|
IdeClient: {
|
|
getInstance: vi.fn(),
|
|
},
|
|
};
|
|
});
|
|
|
|
describe('systemInfo', () => {
|
|
let mockContext: CommandContext;
|
|
const originalPlatform = process.platform;
|
|
const originalArch = process.arch;
|
|
const originalVersion = process.version;
|
|
const originalEnv = { ...process.env };
|
|
|
|
beforeEach(() => {
|
|
mockContext = createMockCommandContext({
|
|
services: {
|
|
config: {
|
|
getModel: vi.fn().mockReturnValue('test-model'),
|
|
getIdeMode: vi.fn().mockReturnValue(true),
|
|
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
|
getAuthType: vi.fn().mockReturnValue('test-auth'),
|
|
getProxy: vi.fn().mockReturnValue(undefined),
|
|
getContentGeneratorConfig: vi.fn().mockReturnValue({
|
|
baseUrl: 'https://api.openai.com',
|
|
}),
|
|
},
|
|
settings: {
|
|
merged: {
|
|
security: {
|
|
auth: {
|
|
selectedType: 'test-auth',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
} as unknown as CommandContext);
|
|
|
|
vi.mocked(versionUtils.getCliVersion).mockResolvedValue('test-version');
|
|
vi.mocked(child_process.execSync).mockImplementation(
|
|
(command: string, options?: ExecSyncOptions) => {
|
|
if (
|
|
options &&
|
|
typeof options === 'object' &&
|
|
'encoding' in options &&
|
|
options.encoding === 'utf-8'
|
|
) {
|
|
return '10.0.0';
|
|
}
|
|
return Buffer.from('10.0.0', 'utf-8');
|
|
},
|
|
);
|
|
vi.mocked(os.release).mockReturnValue('22.0.0');
|
|
process.env['GOOGLE_CLOUD_PROJECT'] = 'test-gcp-project';
|
|
Object.defineProperty(process, 'platform', {
|
|
value: 'test-os',
|
|
});
|
|
Object.defineProperty(process, 'arch', {
|
|
value: 'x64',
|
|
});
|
|
Object.defineProperty(process, 'version', {
|
|
value: 'v20.0.0',
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllEnvs();
|
|
Object.defineProperty(process, 'platform', {
|
|
value: originalPlatform,
|
|
});
|
|
Object.defineProperty(process, 'arch', {
|
|
value: originalArch,
|
|
});
|
|
Object.defineProperty(process, 'version', {
|
|
value: originalVersion,
|
|
});
|
|
process.env = originalEnv;
|
|
vi.clearAllMocks();
|
|
vi.resetAllMocks();
|
|
});
|
|
|
|
describe('getNpmVersion', () => {
|
|
it('should return npm version when available', async () => {
|
|
vi.mocked(child_process.execSync).mockImplementation(
|
|
(command: string, options?: ExecSyncOptions) => {
|
|
if (
|
|
options &&
|
|
typeof options === 'object' &&
|
|
'encoding' in options &&
|
|
options.encoding === 'utf-8'
|
|
) {
|
|
return '10.0.0';
|
|
}
|
|
return Buffer.from('10.0.0', 'utf-8');
|
|
},
|
|
);
|
|
const version = await getNpmVersion();
|
|
expect(version).toBe('10.0.0');
|
|
});
|
|
|
|
it('should return unknown when npm command fails', async () => {
|
|
vi.mocked(child_process.execSync).mockImplementation(() => {
|
|
throw new Error('npm not found');
|
|
});
|
|
const version = await getNpmVersion();
|
|
expect(version).toBe('unknown');
|
|
});
|
|
});
|
|
|
|
describe('getSandboxEnv', () => {
|
|
it('should return "no sandbox" when SANDBOX is not set', () => {
|
|
delete process.env['SANDBOX'];
|
|
expect(getSandboxEnv()).toBe('no sandbox');
|
|
});
|
|
|
|
it('should return sandbox-exec info when SANDBOX is sandbox-exec', () => {
|
|
process.env['SANDBOX'] = 'sandbox-exec';
|
|
process.env['SEATBELT_PROFILE'] = 'test-profile';
|
|
expect(getSandboxEnv()).toBe('sandbox-exec (test-profile)');
|
|
});
|
|
|
|
it('should return sandbox name without prefix when stripPrefix is true', () => {
|
|
process.env['SANDBOX'] = 'qwen-code-test-sandbox';
|
|
expect(getSandboxEnv(true)).toBe('test-sandbox');
|
|
});
|
|
|
|
it('should return sandbox name with prefix when stripPrefix is false', () => {
|
|
process.env['SANDBOX'] = 'qwen-code-test-sandbox';
|
|
expect(getSandboxEnv(false)).toBe('qwen-code-test-sandbox');
|
|
});
|
|
|
|
it('should handle qwen- prefix removal', () => {
|
|
process.env['SANDBOX'] = 'qwen-custom-sandbox';
|
|
expect(getSandboxEnv(true)).toBe('custom-sandbox');
|
|
});
|
|
});
|
|
|
|
describe('getIdeClientName', () => {
|
|
it('should return IDE client name when IDE mode is enabled', async () => {
|
|
vi.mocked(IdeClient.getInstance).mockResolvedValue({
|
|
getDetectedIdeDisplayName: vi.fn().mockReturnValue('test-ide'),
|
|
} as unknown as IdeClient);
|
|
|
|
const ideClient = await getIdeClientName(mockContext);
|
|
expect(ideClient).toBe('test-ide');
|
|
});
|
|
|
|
it('should return empty string when IDE mode is disabled', async () => {
|
|
vi.mocked(mockContext.services.config!.getIdeMode).mockReturnValue(false);
|
|
|
|
const ideClient = await getIdeClientName(mockContext);
|
|
expect(ideClient).toBe('');
|
|
});
|
|
|
|
it('should return empty string when IDE client detection fails', async () => {
|
|
vi.mocked(IdeClient.getInstance).mockRejectedValue(
|
|
new Error('IDE client error'),
|
|
);
|
|
|
|
const ideClient = await getIdeClientName(mockContext);
|
|
expect(ideClient).toBe('');
|
|
});
|
|
});
|
|
|
|
describe('getSystemInfo', () => {
|
|
it('should collect all system information', async () => {
|
|
// Ensure SANDBOX is not set for this test
|
|
delete process.env['SANDBOX'];
|
|
vi.mocked(IdeClient.getInstance).mockResolvedValue({
|
|
getDetectedIdeDisplayName: vi.fn().mockReturnValue('test-ide'),
|
|
} as unknown as IdeClient);
|
|
vi.mocked(child_process.execSync).mockImplementation(
|
|
(command: string, options?: ExecSyncOptions) => {
|
|
if (
|
|
options &&
|
|
typeof options === 'object' &&
|
|
'encoding' in options &&
|
|
options.encoding === 'utf-8'
|
|
) {
|
|
return '10.0.0';
|
|
}
|
|
return Buffer.from('10.0.0', 'utf-8');
|
|
},
|
|
);
|
|
|
|
const systemInfo = await getSystemInfo(mockContext);
|
|
|
|
expect(systemInfo).toEqual({
|
|
cliVersion: 'test-version',
|
|
osPlatform: 'test-os',
|
|
osArch: 'x64',
|
|
osRelease: '22.0.0',
|
|
nodeVersion: 'v20.0.0',
|
|
npmVersion: '10.0.0',
|
|
sandboxEnv: 'no sandbox',
|
|
modelVersion: 'test-model',
|
|
selectedAuthType: 'test-auth',
|
|
ideClient: 'test-ide',
|
|
sessionId: 'test-session-id',
|
|
proxy: undefined,
|
|
});
|
|
});
|
|
|
|
it('should handle missing config gracefully', async () => {
|
|
mockContext.services.config = null;
|
|
vi.mocked(IdeClient.getInstance).mockResolvedValue({
|
|
getDetectedIdeDisplayName: vi.fn().mockReturnValue(''),
|
|
} as unknown as IdeClient);
|
|
|
|
const systemInfo = await getSystemInfo(mockContext);
|
|
|
|
expect(systemInfo.modelVersion).toBe('Unknown');
|
|
expect(systemInfo.sessionId).toBe('unknown');
|
|
});
|
|
});
|
|
|
|
describe('getExtendedSystemInfo', () => {
|
|
it('should include memory usage and base URL', async () => {
|
|
vi.mocked(IdeClient.getInstance).mockResolvedValue({
|
|
getDetectedIdeDisplayName: vi.fn().mockReturnValue('test-ide'),
|
|
} as unknown as IdeClient);
|
|
vi.mocked(child_process.execSync).mockImplementation(
|
|
(command: string, options?: ExecSyncOptions) => {
|
|
if (
|
|
options &&
|
|
typeof options === 'object' &&
|
|
'encoding' in options &&
|
|
options.encoding === 'utf-8'
|
|
) {
|
|
return '10.0.0';
|
|
}
|
|
return Buffer.from('10.0.0', 'utf-8');
|
|
},
|
|
);
|
|
|
|
const { AuthType } = await import('@qwen-code/qwen-code-core');
|
|
// Update the mock context to use OpenAI auth
|
|
mockContext.services.settings.merged.security!.auth!.selectedType =
|
|
AuthType.USE_OPENAI;
|
|
vi.mocked(mockContext.services.config!.getAuthType).mockReturnValue(
|
|
AuthType.USE_OPENAI,
|
|
);
|
|
|
|
const extendedInfo = await getExtendedSystemInfo(mockContext);
|
|
|
|
expect(extendedInfo.memoryUsage).toBeDefined();
|
|
expect(extendedInfo.memoryUsage).toMatch(/\d+\.\d+ (KB|MB|GB)/);
|
|
expect(extendedInfo.baseUrl).toBe('https://api.openai.com');
|
|
});
|
|
|
|
it('should use sandbox env without prefix for bug reports', async () => {
|
|
process.env['SANDBOX'] = 'qwen-code-test-sandbox';
|
|
vi.mocked(IdeClient.getInstance).mockResolvedValue({
|
|
getDetectedIdeDisplayName: vi.fn().mockReturnValue(''),
|
|
} as unknown as IdeClient);
|
|
vi.mocked(child_process.execSync).mockImplementation(
|
|
(command: string, options?: ExecSyncOptions) => {
|
|
if (
|
|
options &&
|
|
typeof options === 'object' &&
|
|
'encoding' in options &&
|
|
options.encoding === 'utf-8'
|
|
) {
|
|
return '10.0.0';
|
|
}
|
|
return Buffer.from('10.0.0', 'utf-8');
|
|
},
|
|
);
|
|
|
|
const extendedInfo = await getExtendedSystemInfo(mockContext);
|
|
|
|
expect(extendedInfo.sandboxEnv).toBe('test-sandbox');
|
|
});
|
|
|
|
it('should not include base URL for non-OpenAI auth', async () => {
|
|
vi.mocked(IdeClient.getInstance).mockResolvedValue({
|
|
getDetectedIdeDisplayName: vi.fn().mockReturnValue(''),
|
|
} as unknown as IdeClient);
|
|
vi.mocked(child_process.execSync).mockImplementation(
|
|
(command: string, options?: ExecSyncOptions) => {
|
|
if (
|
|
options &&
|
|
typeof options === 'object' &&
|
|
'encoding' in options &&
|
|
options.encoding === 'utf-8'
|
|
) {
|
|
return '10.0.0';
|
|
}
|
|
return Buffer.from('10.0.0', 'utf-8');
|
|
},
|
|
);
|
|
|
|
const extendedInfo = await getExtendedSystemInfo(mockContext);
|
|
|
|
expect(extendedInfo.baseUrl).toBeUndefined();
|
|
});
|
|
});
|
|
});
|