mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-21 01:07:46 +00:00
feat(ide ext): Write workspace path to port file (#6659)
This commit is contained in:
287
packages/vscode-ide-companion/src/ide-server.test.ts
Normal file
287
packages/vscode-ide-companion/src/ide-server.test.ts
Normal file
@@ -0,0 +1,287 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type * as vscode from 'vscode';
|
||||
import * as fs from 'node:fs/promises';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
import { IDEServer } from './ide-server.js';
|
||||
import { DiffManager } from './diff-manager.js';
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
diffManager: {
|
||||
onDidChange: vi.fn(() => ({ dispose: vi.fn() })),
|
||||
} as unknown as DiffManager,
|
||||
}));
|
||||
|
||||
vi.mock('node:fs/promises', () => ({
|
||||
writeFile: vi.fn(() => Promise.resolve(undefined)),
|
||||
unlink: vi.fn(() => Promise.resolve(undefined)),
|
||||
}));
|
||||
|
||||
vi.mock('node:os', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof os>();
|
||||
return {
|
||||
...actual,
|
||||
tmpdir: vi.fn(() => '/tmp'),
|
||||
};
|
||||
});
|
||||
|
||||
const vscodeMock = vi.hoisted(() => ({
|
||||
workspace: {
|
||||
workspaceFolders: [
|
||||
{
|
||||
uri: {
|
||||
fsPath: '/test/workspace1',
|
||||
},
|
||||
},
|
||||
{
|
||||
uri: {
|
||||
fsPath: '/test/workspace2',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('vscode', () => vscodeMock);
|
||||
|
||||
vi.mock('./open-files-manager', () => {
|
||||
const OpenFilesManager = vi.fn();
|
||||
OpenFilesManager.prototype.onDidChange = vi.fn(() => ({ dispose: vi.fn() }));
|
||||
return { OpenFilesManager };
|
||||
});
|
||||
|
||||
describe('IDEServer', () => {
|
||||
let ideServer: IDEServer;
|
||||
let mockContext: vscode.ExtensionContext;
|
||||
let mockLog: (message: string) => void;
|
||||
|
||||
const getPortFromMock = (
|
||||
replaceMock: ReturnType<
|
||||
() => vscode.ExtensionContext['environmentVariableCollection']['replace']
|
||||
>,
|
||||
) => {
|
||||
const port = vi
|
||||
.mocked(replaceMock)
|
||||
.mock.calls.find((call) => call[0] === 'GEMINI_CLI_IDE_SERVER_PORT')?.[1];
|
||||
|
||||
if (port === undefined) {
|
||||
expect.fail('Port was not set');
|
||||
}
|
||||
return port;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockLog = vi.fn();
|
||||
ideServer = new IDEServer(mockLog, mocks.diffManager);
|
||||
mockContext = {
|
||||
subscriptions: [],
|
||||
environmentVariableCollection: {
|
||||
replace: vi.fn(),
|
||||
clear: vi.fn(),
|
||||
},
|
||||
} as unknown as vscode.ExtensionContext;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await ideServer.stop();
|
||||
vi.restoreAllMocks();
|
||||
vscodeMock.workspace.workspaceFolders = [
|
||||
{ uri: { fsPath: '/test/workspace1' } },
|
||||
{ uri: { fsPath: '/test/workspace2' } },
|
||||
];
|
||||
});
|
||||
|
||||
it('should set environment variables and workspace path on start with multiple folders', async () => {
|
||||
await ideServer.start(mockContext);
|
||||
|
||||
const replaceMock = mockContext.environmentVariableCollection.replace;
|
||||
expect(replaceMock).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(replaceMock).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'GEMINI_CLI_IDE_SERVER_PORT',
|
||||
expect.any(String), // port is a number as a string
|
||||
);
|
||||
|
||||
const expectedWorkspacePaths = [
|
||||
'/test/workspace1',
|
||||
'/test/workspace2',
|
||||
].join(path.delimiter);
|
||||
|
||||
expect(replaceMock).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'GEMINI_CLI_IDE_WORKSPACE_PATH',
|
||||
expectedWorkspacePaths,
|
||||
);
|
||||
|
||||
const port = getPortFromMock(replaceMock);
|
||||
const expectedPortFile = path.join(
|
||||
'/tmp',
|
||||
`gemini-ide-server-${process.ppid}.json`,
|
||||
);
|
||||
expect(fs.writeFile).toHaveBeenCalledWith(
|
||||
expectedPortFile,
|
||||
JSON.stringify({
|
||||
port: parseInt(port, 10),
|
||||
workspacePath: expectedWorkspacePaths,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should set a single folder path', async () => {
|
||||
vscodeMock.workspace.workspaceFolders = [{ uri: { fsPath: '/foo/bar' } }];
|
||||
|
||||
await ideServer.start(mockContext);
|
||||
const replaceMock = mockContext.environmentVariableCollection.replace;
|
||||
|
||||
expect(replaceMock).toHaveBeenCalledWith(
|
||||
'GEMINI_CLI_IDE_WORKSPACE_PATH',
|
||||
'/foo/bar',
|
||||
);
|
||||
|
||||
const port = getPortFromMock(replaceMock);
|
||||
const expectedPortFile = path.join(
|
||||
'/tmp',
|
||||
`gemini-ide-server-${process.ppid}.json`,
|
||||
);
|
||||
expect(fs.writeFile).toHaveBeenCalledWith(
|
||||
expectedPortFile,
|
||||
JSON.stringify({
|
||||
port: parseInt(port, 10),
|
||||
workspacePath: '/foo/bar',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should set an empty string if no folders are open', async () => {
|
||||
vscodeMock.workspace.workspaceFolders = [];
|
||||
|
||||
await ideServer.start(mockContext);
|
||||
const replaceMock = mockContext.environmentVariableCollection.replace;
|
||||
|
||||
expect(replaceMock).toHaveBeenCalledWith(
|
||||
'GEMINI_CLI_IDE_WORKSPACE_PATH',
|
||||
'',
|
||||
);
|
||||
|
||||
const port = getPortFromMock(replaceMock);
|
||||
const expectedPortFile = path.join(
|
||||
'/tmp',
|
||||
`gemini-ide-server-${process.ppid}.json`,
|
||||
);
|
||||
expect(fs.writeFile).toHaveBeenCalledWith(
|
||||
expectedPortFile,
|
||||
JSON.stringify({
|
||||
port: parseInt(port, 10),
|
||||
workspacePath: '',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should update the path when workspace folders change', async () => {
|
||||
vscodeMock.workspace.workspaceFolders = [{ uri: { fsPath: '/foo/bar' } }];
|
||||
await ideServer.start(mockContext);
|
||||
const replaceMock = mockContext.environmentVariableCollection.replace;
|
||||
|
||||
expect(replaceMock).toHaveBeenCalledWith(
|
||||
'GEMINI_CLI_IDE_WORKSPACE_PATH',
|
||||
'/foo/bar',
|
||||
);
|
||||
|
||||
// Simulate adding a folder
|
||||
vscodeMock.workspace.workspaceFolders = [
|
||||
{ uri: { fsPath: '/foo/bar' } },
|
||||
{ uri: { fsPath: '/baz/qux' } },
|
||||
];
|
||||
await ideServer.updateWorkspacePath();
|
||||
|
||||
const expectedWorkspacePaths = ['/foo/bar', '/baz/qux'].join(
|
||||
path.delimiter,
|
||||
);
|
||||
expect(replaceMock).toHaveBeenCalledWith(
|
||||
'GEMINI_CLI_IDE_WORKSPACE_PATH',
|
||||
expectedWorkspacePaths,
|
||||
);
|
||||
|
||||
const port = getPortFromMock(replaceMock);
|
||||
const expectedPortFile = path.join(
|
||||
'/tmp',
|
||||
`gemini-ide-server-${process.ppid}.json`,
|
||||
);
|
||||
expect(fs.writeFile).toHaveBeenCalledWith(
|
||||
expectedPortFile,
|
||||
JSON.stringify({
|
||||
port: parseInt(port, 10),
|
||||
workspacePath: expectedWorkspacePaths,
|
||||
}),
|
||||
);
|
||||
|
||||
// Simulate removing a folder
|
||||
vscodeMock.workspace.workspaceFolders = [{ uri: { fsPath: '/baz/qux' } }];
|
||||
await ideServer.updateWorkspacePath();
|
||||
|
||||
expect(replaceMock).toHaveBeenCalledWith(
|
||||
'GEMINI_CLI_IDE_WORKSPACE_PATH',
|
||||
'/baz/qux',
|
||||
);
|
||||
expect(fs.writeFile).toHaveBeenCalledWith(
|
||||
expectedPortFile,
|
||||
JSON.stringify({
|
||||
port: parseInt(port, 10),
|
||||
workspacePath: '/baz/qux',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should clear env vars and delete port file on stop', async () => {
|
||||
await ideServer.start(mockContext);
|
||||
const portFile = path.join(
|
||||
'/tmp',
|
||||
`gemini-ide-server-${process.ppid}.json`,
|
||||
);
|
||||
expect(fs.writeFile).toHaveBeenCalledWith(portFile, expect.any(String));
|
||||
|
||||
await ideServer.stop();
|
||||
|
||||
expect(mockContext.environmentVariableCollection.clear).toHaveBeenCalled();
|
||||
expect(fs.unlink).toHaveBeenCalledWith(portFile);
|
||||
});
|
||||
|
||||
it.skipIf(process.platform !== 'win32')(
|
||||
'should handle windows paths',
|
||||
async () => {
|
||||
vscodeMock.workspace.workspaceFolders = [
|
||||
{ uri: { fsPath: 'c:\\foo\\bar' } },
|
||||
{ uri: { fsPath: 'd:\\baz\\qux' } },
|
||||
];
|
||||
|
||||
await ideServer.start(mockContext);
|
||||
const replaceMock = mockContext.environmentVariableCollection.replace;
|
||||
const expectedWorkspacePaths = 'c:\\foo\\bar;d:\\baz\\qux';
|
||||
|
||||
expect(replaceMock).toHaveBeenCalledWith(
|
||||
'GEMINI_CLI_IDE_WORKSPACE_PATH',
|
||||
expectedWorkspacePaths,
|
||||
);
|
||||
|
||||
const port = getPortFromMock(replaceMock);
|
||||
const expectedPortFile = path.join(
|
||||
'/tmp',
|
||||
`gemini-ide-server-${process.ppid}.json`,
|
||||
);
|
||||
expect(fs.writeFile).toHaveBeenCalledWith(
|
||||
expectedPortFile,
|
||||
JSON.stringify({
|
||||
port: parseInt(port, 10),
|
||||
workspacePath: expectedWorkspacePaths,
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user