Skip to content

Commit d7ac09f

Browse files
committed
fix: tests
1 parent e925caf commit d7ac09f

3 files changed

Lines changed: 146 additions & 44 deletions

File tree

src/test/extension.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@ import * as sinon from 'sinon';
88
import { setupTestEnvironment, getTestBinaryPathResolver } from './testUtils';
99

1010
suite('Extension Test Suite', () => {
11-
// Setup test environment
12-
const { sandbox, cleanup } = setupTestEnvironment();
11+
let testEnv: ReturnType<typeof setupTestEnvironment>;
1312
let getBinaryPathStub: sinon.SinonStub;
1413

1514
setup(() => {
15+
testEnv = setupTestEnvironment();
1616
// Stub getBinaryPath to prevent errors when the server tries to start
1717
getBinaryPathStub = sinon.stub(require('../utils/binaryPath'), 'getBinaryPath');
18-
getBinaryPathStub.callsFake(getTestBinaryPathResolver());
18+
getBinaryPathStub.callsFake(getTestBinaryPathResolver(testEnv.context));
1919
});
2020

2121
teardown(() => {
2222
getBinaryPathStub.restore();
23-
cleanup();
23+
testEnv.cleanup();
2424
});
2525

2626
test('Extension should be present', () => {

src/test/testUtils.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import * as vscode from 'vscode';
2+
import { ExtensionContext } from 'vscode';
3+
import { getBinaryPath } from '../utils/binaryPath';
4+
import * as sinon from 'sinon';
5+
6+
export interface TestEnvironment {
7+
context: ExtensionContext;
8+
sandbox: sinon.SinonSandbox;
9+
cleanup: () => void;
10+
}
11+
12+
/**
13+
* Sets up a test environment with mock VSCode extension context
14+
*/
15+
export function setupTestEnvironment(): TestEnvironment {
16+
const sandbox = sinon.createSandbox();
17+
18+
const context = {
19+
subscriptions: [],
20+
extensionPath: '',
21+
extensionUri: vscode.Uri.parse('file:///test'),
22+
environmentVariableCollection: {
23+
persistent: false,
24+
replace: () => { },
25+
append: () => { },
26+
prepend: () => { },
27+
get: () => undefined,
28+
forEach: () => { },
29+
delete: () => { },
30+
has: () => false,
31+
clear: () => { },
32+
getScoped: () => ({} as vscode.EnvironmentVariableCollection),
33+
description: undefined,
34+
[Symbol.iterator]: function* () { yield* []; }
35+
},
36+
storageUri: vscode.Uri.parse('file:///test/storage'),
37+
globalStorageUri: vscode.Uri.parse('file:///test/global-storage'),
38+
logUri: vscode.Uri.parse('file:///test/logs'),
39+
extensionMode: vscode.ExtensionMode.Test,
40+
isNewInstall: false,
41+
extension: {
42+
id: 'test-extension',
43+
extensionUri: vscode.Uri.parse('file:///test'),
44+
isActive: true,
45+
packageJSON: {},
46+
extensionPath: '',
47+
extensionKind: vscode.ExtensionKind.UI,
48+
exports: {},
49+
activate: () => Promise.resolve({})
50+
},
51+
workspaceState: {
52+
get: () => undefined,
53+
update: () => Promise.resolve(),
54+
keys: () => []
55+
},
56+
globalState: {
57+
get: () => undefined,
58+
update: () => Promise.resolve(),
59+
setKeysForSync: () => { },
60+
keys: () => []
61+
},
62+
secrets: {
63+
get: () => Promise.resolve(undefined),
64+
store: () => Promise.resolve(),
65+
delete: () => Promise.resolve(),
66+
onDidChange: new vscode.EventEmitter().event
67+
},
68+
asAbsolutePath: (relativePath: string) => relativePath,
69+
storagePath: undefined,
70+
globalStoragePath: undefined,
71+
logPath: undefined,
72+
extensionRuntime: 1,
73+
languageModelAccessInformation: {
74+
canAccessLanguageModels: false,
75+
onDidChangeLanguageModelAccess: new vscode.EventEmitter().event
76+
}
77+
} as unknown as ExtensionContext;
78+
79+
const cleanup = () => {
80+
sandbox.restore();
81+
};
82+
83+
return { context, sandbox, cleanup };
84+
}
85+
86+
/**
87+
* Creates a silent logger for testing
88+
*/
89+
export const silentLogger = {
90+
info: () => { },
91+
error: () => { },
92+
debug: () => { },
93+
warn: () => { }
94+
};
95+
96+
/**
97+
* Creates a test binary path resolver
98+
*/
99+
export function getTestBinaryPathResolver(context: ExtensionContext): (binaryName: string) => string {
100+
return (binaryName: string) => getBinaryPath(context, binaryName);
101+
}

src/test/unit/serverManager.test.ts

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,30 @@ suite('ServerManager Tests', () => {
1717
let mockApiClient: any;
1818
let mockProcess: any;
1919
let getBinaryPathStub: sinon.SinonStub;
20-
21-
// Setup test environment
22-
const { sandbox, cleanup } = setupTestEnvironment();
20+
let testEnv: ReturnType<typeof setupTestEnvironment>;
2321

2422
setup(() => {
23+
testEnv = setupTestEnvironment();
24+
mockContext = testEnv.context;
25+
2526
// Stub binary path resolver
2627
getBinaryPathStub = sinon.stub(require('../../utils/binaryPath'), 'getBinaryPath');
27-
getBinaryPathStub.callsFake(getTestBinaryPathResolver());
28+
getBinaryPathStub.callsFake(getTestBinaryPathResolver(testEnv.context));
2829

2930
// Create mock process using Object.create and assign properties
3031
mockProcess = Object.create(EventEmitter.prototype);
3132
Object.assign(mockProcess, {
32-
kill: sandbox.stub(),
33+
kill: testEnv.sandbox.stub(),
3334
pid: 12345,
3435
stdin: null,
35-
stdout: Object.assign(new EventEmitter(), { pipe: sandbox.stub() }),
36-
stderr: Object.assign(new EventEmitter(), { pipe: sandbox.stub() }),
36+
stdout: Object.assign(new EventEmitter(), { pipe: testEnv.sandbox.stub() }),
37+
stderr: Object.assign(new EventEmitter(), { pipe: testEnv.sandbox.stub() }),
3738
stdio: [null, null, null, null, null],
38-
unref: sandbox.stub(),
39-
ref: sandbox.stub(),
39+
unref: testEnv.sandbox.stub(),
40+
ref: testEnv.sandbox.stub(),
4041
connected: false,
41-
disconnect: sandbox.stub(),
42-
send: sandbox.stub(),
42+
disconnect: testEnv.sandbox.stub(),
43+
send: testEnv.sandbox.stub(),
4344
channel: null,
4445
spawnargs: [],
4546
spawnfile: '',
@@ -52,7 +53,7 @@ suite('ServerManager Tests', () => {
5253
});
5354

5455
// Create the startGoosed stub function manually
55-
startGoosedStub = sandbox.stub<Parameters<typeof actualGooseServer.startGoosed>, Promise<actualGooseServer.GooseServerInfo>>();
56+
startGoosedStub = testEnv.sandbox.stub<Parameters<typeof actualGooseServer.startGoosed>, Promise<actualGooseServer.GooseServerInfo>>();
5657
startGoosedStub.resolves({
5758
port: 8000,
5859
workingDir: path.resolve(__dirname, '../../../test-workspace'),
@@ -67,9 +68,9 @@ suite('ServerManager Tests', () => {
6768
asAbsolutePath: (relativePath: string) => path.resolve(__dirname, '../../../', relativePath),
6869
storageUri: undefined,
6970
globalState: {
70-
get: sandbox.stub(),
71-
update: sandbox.stub(),
72-
setKeysForSync: sandbox.stub()
71+
get: testEnv.sandbox.stub(),
72+
update: testEnv.sandbox.stub(),
73+
setKeysForSync: testEnv.sandbox.stub()
7374
} as unknown as vscode.Memento & { setKeysForSync(keys: readonly string[]): void },
7475
workspaceState: {} as vscode.Memento,
7576
secrets: {} as vscode.SecretStorage,
@@ -85,22 +86,22 @@ suite('ServerManager Tests', () => {
8586
name: 'Test Workspace',
8687
index: 0
8788
};
88-
workspaceFoldersStub = sandbox.stub(vscode.workspace, 'workspaceFolders');
89+
workspaceFoldersStub = testEnv.sandbox.stub(vscode.workspace, 'workspaceFolders');
8990
workspaceFoldersStub.value([mockWorkspaceFolder]);
9091

9192
// Mock the ApiClient constructor
9293
mockApiClient = {
93-
getAgentVersions: sandbox.stub().resolves({ versions: ['1.0.0', '2.0.0'] }),
94-
createAgent: sandbox.stub().resolves({ id: 'test-agent-id' }),
95-
request: sandbox.stub().resolves({}),
96-
getConversations: sandbox.stub().resolves([]),
97-
createConversation: sandbox.stub().resolves({ id: 'test-conversation-id' }),
98-
sendMessage: sandbox.stub().resolves({ id: 'test-message-id' }),
99-
getConfiguration: sandbox.stub().resolves({}),
100-
updateConfiguration: sandbox.stub().resolves({}),
101-
checkStatus: sandbox.stub().resolves(true),
102-
addExtension: sandbox.stub().resolves({ id: 'test-extension-id' }),
103-
streamMessage: sandbox.stub().callsFake(() => {
94+
getAgentVersions: testEnv.sandbox.stub().resolves({ versions: ['1.0.0', '2.0.0'] }),
95+
createAgent: testEnv.sandbox.stub().resolves({ id: 'test-agent-id' }),
96+
request: testEnv.sandbox.stub().resolves({}),
97+
getConversations: testEnv.sandbox.stub().resolves([]),
98+
createConversation: testEnv.sandbox.stub().resolves({ id: 'test-conversation-id' }),
99+
sendMessage: testEnv.sandbox.stub().resolves({ id: 'test-message-id' }),
100+
getConfiguration: testEnv.sandbox.stub().resolves({}),
101+
updateConfiguration: testEnv.sandbox.stub().resolves({}),
102+
checkStatus: testEnv.sandbox.stub().resolves(true),
103+
addExtension: testEnv.sandbox.stub().resolves({ id: 'test-extension-id' }),
104+
streamMessage: testEnv.sandbox.stub().callsFake(() => {
104105
const emitter = new EventEmitter();
105106
setTimeout(() => {
106107
emitter.emit('data', { content: 'test content' });
@@ -115,7 +116,7 @@ suite('ServerManager Tests', () => {
115116
Object.assign(this, mockApiClient);
116117
}
117118
}
118-
sandbox.stub(require('../../server/apiClient'), 'ApiClient').value(MockApiClient);
119+
testEnv.sandbox.stub(require('../../server/apiClient'), 'ApiClient').value(MockApiClient);
119120

120121
// Create the server manager with silent logger
121122
serverManager = new ServerManager(mockContext as vscode.ExtensionContext, startGoosedStub);
@@ -126,15 +127,15 @@ suite('ServerManager Tests', () => {
126127

127128
teardown(() => {
128129
getBinaryPathStub.restore();
129-
cleanup();
130+
testEnv.cleanup();
130131
});
131132

132133
test('should have stopped status initially', () => {
133134
assert.strictEqual(serverManager.getStatus(), ServerStatus.STOPPED);
134135
});
135136

136137
test('should emit status change events', async () => {
137-
const statusChangeListener = sandbox.spy();
138+
const statusChangeListener = testEnv.sandbox.spy();
138139
serverManager.on(ServerEvents.STATUS_CHANGE, statusChangeListener);
139140
await serverManager.start();
140141

@@ -160,8 +161,8 @@ suite('ServerManager Tests', () => {
160161
});
161162

162163
test('should handle errors during server start', async () => {
163-
sandbox.stub(serverManager as any, 'getWorkspaceDirectory').throws(new Error('Workspace directory error'));
164-
const errorListener = sandbox.spy();
164+
testEnv.sandbox.stub(serverManager as any, 'getWorkspaceDirectory').throws(new Error('Workspace directory error'));
165+
const errorListener = testEnv.sandbox.spy();
165166
serverManager.on(ServerEvents.ERROR, errorListener);
166167

167168
const result = await serverManager.start();
@@ -174,7 +175,7 @@ suite('ServerManager Tests', () => {
174175

175176
test('should handle server start failure gracefully', async () => {
176177
startGoosedStub.rejects(new Error('Test error from injected stub'));
177-
const errorListener = sandbox.spy();
178+
const errorListener = testEnv.sandbox.spy();
178179
serverManager.on(ServerEvents.ERROR, errorListener);
179180

180181
const result = await serverManager.start();
@@ -188,10 +189,10 @@ suite('ServerManager Tests', () => {
188189
test('should handle server process exit', async () => {
189190
await serverManager.start();
190191

191-
const exitListener = sandbox.spy();
192+
const exitListener = testEnv.sandbox.spy();
192193
serverManager.on(ServerEvents.SERVER_EXIT, exitListener);
193194

194-
const statusChangeListener = sandbox.spy();
195+
const statusChangeListener = testEnv.sandbox.spy();
195196
serverManager.on(ServerEvents.STATUS_CHANGE, statusChangeListener);
196197

197198
const serverProcess = (serverManager as any).serverInfo.process;
@@ -204,10 +205,10 @@ suite('ServerManager Tests', () => {
204205
test('should handle server process crash', async () => {
205206
await serverManager.start();
206207

207-
const exitListener = sandbox.spy();
208+
const exitListener = testEnv.sandbox.spy();
208209
serverManager.on(ServerEvents.SERVER_EXIT, exitListener);
209210

210-
const statusChangeListener = sandbox.spy();
211+
const statusChangeListener = testEnv.sandbox.spy();
211212
serverManager.on(ServerEvents.STATUS_CHANGE, statusChangeListener);
212213

213214
const serverProcess = (serverManager as any).serverInfo.process;
@@ -220,10 +221,10 @@ suite('ServerManager Tests', () => {
220221
test('should handle server process exit with null code', async () => {
221222
await serverManager.start();
222223

223-
const exitListener = sandbox.spy();
224+
const exitListener = testEnv.sandbox.spy();
224225
serverManager.on(ServerEvents.SERVER_EXIT, exitListener);
225226

226-
const statusChangeListener = sandbox.spy();
227+
const statusChangeListener = testEnv.sandbox.spy();
227228
serverManager.on(ServerEvents.STATUS_CHANGE, statusChangeListener);
228229

229230
const serverProcess = (serverManager as any).serverInfo.process;
@@ -236,7 +237,7 @@ suite('ServerManager Tests', () => {
236237
test('should not emit server exit event when stopping server manually', async () => {
237238
await serverManager.start();
238239

239-
const exitListener = sandbox.spy();
240+
const exitListener = testEnv.sandbox.spy();
240241
serverManager.on(ServerEvents.SERVER_EXIT, exitListener);
241242

242243
await serverManager.stop();

0 commit comments

Comments
 (0)