mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-17 01:13:32 +01:00
fix tests
This commit is contained in:
@@ -26,6 +26,9 @@ import { DefaultURITransformer } from '../../../base/common/uriIpc.js';
|
||||
import product from '../../product/common/product.js';
|
||||
import { IProductService } from '../../product/common/productService.js';
|
||||
import { localize } from '../../../nls.js';
|
||||
import { FileService } from '../../files/common/fileService.js';
|
||||
import { DiskFileSystemProvider } from '../../files/node/diskFileSystemProvider.js';
|
||||
import { Schemas } from '../../../base/common/network.js';
|
||||
|
||||
// Entry point for the agent host utility process.
|
||||
// Sets up IPC, logging, and registers agent providers (Copilot).
|
||||
@@ -54,10 +57,14 @@ function startAgentHost(): void {
|
||||
const logService = new LogService(logger);
|
||||
logService.info('Agent Host process started successfully');
|
||||
|
||||
// File service
|
||||
const fileService = disposables.add(new FileService(logService));
|
||||
disposables.add(fileService.registerProvider(Schemas.file, disposables.add(new DiskFileSystemProvider(logService))));
|
||||
|
||||
// Create the real service implementation that lives in this process
|
||||
let agentService: AgentService;
|
||||
try {
|
||||
agentService = new AgentService(logService);
|
||||
agentService = new AgentService(logService, fileService);
|
||||
agentService.registerProvider(new CopilotAgent(logService));
|
||||
} catch (err) {
|
||||
logService.error('Failed to create AgentService', err);
|
||||
|
||||
@@ -34,6 +34,9 @@ import { AgentSideEffects } from './agentSideEffects.js';
|
||||
import { SessionStateManager } from './sessionStateManager.js';
|
||||
import { WebSocketProtocolServer } from './webSocketTransport.js';
|
||||
import { ProtocolServerHandler } from './protocolServerHandler.js';
|
||||
import { FileService } from '../../files/common/fileService.js';
|
||||
import { DiskFileSystemProvider } from '../../files/node/diskFileSystemProvider.js';
|
||||
import { Schemas } from '../../../base/common/network.js';
|
||||
|
||||
/** Log to stderr so messages appear in the terminal alongside the process. */
|
||||
function log(msg: string): void {
|
||||
@@ -140,6 +143,10 @@ async function main(): Promise<void> {
|
||||
// Observable agents list for root state
|
||||
const registeredAgents = observableValue<readonly IAgent[]>('agents', []);
|
||||
|
||||
// File service
|
||||
const fileService = disposables.add(new FileService(logService));
|
||||
disposables.add(fileService.registerProvider(Schemas.file, disposables.add(new DiskFileSystemProvider(logService))));
|
||||
|
||||
// Shared side-effect handler
|
||||
const sideEffects = disposables.add(new AgentSideEffects(stateManager, {
|
||||
getAgent(session) {
|
||||
@@ -147,7 +154,7 @@ async function main(): Promise<void> {
|
||||
return provider ? agents.get(provider) : agents.values().next().value;
|
||||
},
|
||||
agents: registeredAgents,
|
||||
}, logService));
|
||||
}, logService, fileService));
|
||||
|
||||
function registerAgent(agent: IAgent): void {
|
||||
agents.set(agent.id, agent);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Disposable, DisposableStore } from '../../../base/common/lifecycle.js';
|
||||
import { observableValue } from '../../../base/common/observable.js';
|
||||
import { URI } from '../../../base/common/uri.js';
|
||||
import { ILogService } from '../../log/common/log.js';
|
||||
import { IFileService } from '../../files/common/files.js';
|
||||
import { AgentProvider, IAgentCreateSessionConfig, IAgent, IAgentService, IAgentSessionMetadata, AgentSession, IAgentDescriptor } from '../common/agentService.js';
|
||||
import type { IActionEnvelope, INotification, ISessionAction } from '../common/state/sessionActions.js';
|
||||
import type { IBrowseDirectoryResult, IStateSnapshot } from '../common/state/sessionProtocol.js';
|
||||
@@ -52,6 +53,7 @@ export class AgentService extends Disposable implements IAgentService {
|
||||
|
||||
constructor(
|
||||
private readonly _logService: ILogService,
|
||||
private readonly _fileService: IFileService,
|
||||
) {
|
||||
super();
|
||||
this._logService.info('AgentService initialized');
|
||||
@@ -61,7 +63,7 @@ export class AgentService extends Disposable implements IAgentService {
|
||||
this._sideEffects = this._register(new AgentSideEffects(this._stateManager, {
|
||||
getAgent: session => this._findProviderForSession(session),
|
||||
agents: this._agents,
|
||||
}, this._logService));
|
||||
}, this._logService, this._fileService));
|
||||
}
|
||||
|
||||
// ---- provider registration ----------------------------------------------
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
import { Disposable, DisposableStore, IDisposable } from '../../../base/common/lifecycle.js';
|
||||
import { autorun, IObservable } from '../../../base/common/observable.js';
|
||||
import { URI } from '../../../base/common/uri.js';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import { IFileService } from '../../files/common/files.js';
|
||||
import { ILogService } from '../../log/common/log.js';
|
||||
import { AgentProvider, IAgent, IAgentAttachment } from '../common/agentService.js';
|
||||
import type { ISessionAction } from '../common/state/sessionActions.js';
|
||||
@@ -49,6 +49,7 @@ export class AgentSideEffects extends Disposable implements IProtocolSideEffectH
|
||||
private readonly _stateManager: SessionStateManager,
|
||||
private readonly _options: IAgentSideEffectsOptions,
|
||||
private readonly _logService: ILogService,
|
||||
private readonly _fileService: IFileService,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -234,27 +235,21 @@ export class AgentSideEffects extends Disposable implements IProtocolSideEffectH
|
||||
}
|
||||
|
||||
async handleBrowseDirectory(uri: URI): Promise<IBrowseDirectoryResult> {
|
||||
const dirPath = uri.fsPath || '/';
|
||||
let dirents;
|
||||
let stat;
|
||||
try {
|
||||
dirents = await fs.promises.readdir(dirPath, { withFileTypes: true });
|
||||
} catch (err) {
|
||||
const code = (err as NodeJS.ErrnoException).code;
|
||||
if (code === 'ENOENT') {
|
||||
throw new ProtocolError(JSON_RPC_INTERNAL_ERROR, `Directory not found: ${uri.toString()}`);
|
||||
}
|
||||
if (code === 'ENOTDIR') {
|
||||
throw new ProtocolError(JSON_RPC_INTERNAL_ERROR, `Not a directory: ${uri.toString()}`);
|
||||
}
|
||||
if (code === 'EACCES' || code === 'EPERM') {
|
||||
throw new ProtocolError(JSON_RPC_INTERNAL_ERROR, `Cannot access directory: ${uri.toString()}`);
|
||||
}
|
||||
throw err;
|
||||
stat = await this._fileService.resolve(uri);
|
||||
} catch {
|
||||
throw new ProtocolError(JSON_RPC_INTERNAL_ERROR, `Directory not found: ${uri.toString()}`);
|
||||
}
|
||||
const entries = dirents.map(dirent => {
|
||||
const type: IDirectoryEntry['type'] = dirent.isDirectory() ? 'directory' : 'file';
|
||||
return { name: dirent.name, type };
|
||||
});
|
||||
|
||||
if (!stat.isDirectory) {
|
||||
throw new ProtocolError(JSON_RPC_INTERNAL_ERROR, `Not a directory: ${uri.toString()}`);
|
||||
}
|
||||
|
||||
const entries: IDirectoryEntry[] = (stat.children ?? []).map(child => ({
|
||||
name: child.name,
|
||||
type: child.isDirectory ? 'directory' : 'file',
|
||||
}));
|
||||
return { entries };
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { DisposableStore, toDisposable } from '../../../../base/common/lifecycle
|
||||
import { URI } from '../../../../base/common/uri.js';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
|
||||
import { NullLogService } from '../../../log/common/log.js';
|
||||
import { FileService } from '../../../files/common/fileService.js';
|
||||
import { AgentSession } from '../../common/agentService.js';
|
||||
import { IActionEnvelope } from '../../common/state/sessionActions.js';
|
||||
import { AgentService } from '../../node/agentService.js';
|
||||
@@ -20,7 +21,7 @@ suite('AgentService (node dispatcher)', () => {
|
||||
let copilotAgent: MockAgent;
|
||||
|
||||
setup(() => {
|
||||
service = disposables.add(new AgentService(new NullLogService()));
|
||||
service = disposables.add(new AgentService(new NullLogService(), disposables.add(new FileService(new NullLogService()))));
|
||||
copilotAgent = new MockAgent('copilot');
|
||||
disposables.add(toDisposable(() => copilotAgent.dispose()));
|
||||
});
|
||||
|
||||
@@ -4,10 +4,14 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import assert from 'assert';
|
||||
import { VSBuffer } from '../../../../base/common/buffer.js';
|
||||
import { DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js';
|
||||
import { Schemas } from '../../../../base/common/network.js';
|
||||
import { observableValue } from '../../../../base/common/observable.js';
|
||||
import { URI } from '../../../../base/common/uri.js';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
|
||||
import { FileService } from '../../../files/common/fileService.js';
|
||||
import { InMemoryFileSystemProvider } from '../../../files/common/inMemoryFilesystemProvider.js';
|
||||
import { NullLogService } from '../../../log/common/log.js';
|
||||
import { AgentSession, IAgent } from '../../common/agentService.js';
|
||||
import { IActionEnvelope, ISessionAction } from '../../common/state/sessionActions.js';
|
||||
@@ -15,13 +19,13 @@ import { SessionStatus } from '../../common/state/sessionState.js';
|
||||
import { AgentSideEffects } from '../../node/agentSideEffects.js';
|
||||
import { SessionStateManager } from '../../node/sessionStateManager.js';
|
||||
import { MockAgent } from './mockAgent.js';
|
||||
import { cwd } from '../../../../base/common/process.js';
|
||||
|
||||
// ---- Tests ------------------------------------------------------------------
|
||||
|
||||
suite('AgentSideEffects', () => {
|
||||
|
||||
const disposables = new DisposableStore();
|
||||
let fileService: FileService;
|
||||
let stateManager: SessionStateManager;
|
||||
let agent: MockAgent;
|
||||
let sideEffects: AgentSideEffects;
|
||||
@@ -48,7 +52,16 @@ suite('AgentSideEffects', () => {
|
||||
);
|
||||
}
|
||||
|
||||
setup(() => {
|
||||
setup(async () => {
|
||||
fileService = disposables.add(new FileService(new NullLogService()));
|
||||
const memFs = disposables.add(new InMemoryFileSystemProvider());
|
||||
disposables.add(fileService.registerProvider(Schemas.inMemory, memFs));
|
||||
|
||||
// Seed a file so the handleBrowseDirectory tests can distinguish files from dirs
|
||||
const testDir = URI.from({ scheme: Schemas.inMemory, path: '/testDir' });
|
||||
await fileService.createFolder(testDir);
|
||||
await fileService.writeFile(URI.from({ scheme: Schemas.inMemory, path: '/testDir/file.txt' }), VSBuffer.fromString('hello'));
|
||||
|
||||
agent = new MockAgent();
|
||||
disposables.add(toDisposable(() => agent.dispose()));
|
||||
stateManager = disposables.add(new SessionStateManager(new NullLogService()));
|
||||
@@ -56,10 +69,12 @@ suite('AgentSideEffects', () => {
|
||||
sideEffects = disposables.add(new AgentSideEffects(stateManager, {
|
||||
getAgent: () => agent,
|
||||
agents: agentList,
|
||||
}, new NullLogService()));
|
||||
}, new NullLogService(), fileService));
|
||||
});
|
||||
|
||||
teardown(() => disposables.clear());
|
||||
teardown(() => {
|
||||
disposables.clear();
|
||||
});
|
||||
ensureNoDisposablesAreLeakedInTestSuite();
|
||||
|
||||
// ---- handleAction: session/turnStarted ------------------------------
|
||||
@@ -88,7 +103,7 @@ suite('AgentSideEffects', () => {
|
||||
const noAgentSideEffects = disposables.add(new AgentSideEffects(stateManager, {
|
||||
getAgent: () => undefined,
|
||||
agents: emptyAgents,
|
||||
}, new NullLogService()));
|
||||
}, new NullLogService(), fileService));
|
||||
|
||||
const envelopes: IActionEnvelope[] = [];
|
||||
disposables.add(stateManager.onDidEmitEnvelope(e => envelopes.push(e)));
|
||||
@@ -233,7 +248,7 @@ suite('AgentSideEffects', () => {
|
||||
const noAgentSideEffects = disposables.add(new AgentSideEffects(stateManager, {
|
||||
getAgent: () => undefined,
|
||||
agents: emptyAgents,
|
||||
}, new NullLogService()));
|
||||
}, new NullLogService(), fileService));
|
||||
|
||||
await assert.rejects(
|
||||
() => noAgentSideEffects.handleCreateSession({ session: sessionUri, provider: 'nonexistent' }),
|
||||
@@ -277,14 +292,14 @@ suite('AgentSideEffects', () => {
|
||||
|
||||
test('throws when the directory does not exist', async () => {
|
||||
await assert.rejects(
|
||||
() => sideEffects.handleBrowseDirectory(URI.file('/path/that/does/not/exist')),
|
||||
() => sideEffects.handleBrowseDirectory(URI.from({ scheme: Schemas.inMemory, path: '/nonexistent' })),
|
||||
/Directory not found/,
|
||||
);
|
||||
});
|
||||
|
||||
test('throws when the target is not a directory', async () => {
|
||||
await assert.rejects(
|
||||
() => sideEffects.handleBrowseDirectory(URI.file(cwd() + '/package.json')),
|
||||
() => sideEffects.handleBrowseDirectory(URI.from({ scheme: Schemas.inMemory, path: '/testDir/file.txt' })),
|
||||
/Not a directory/,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -342,7 +342,7 @@ suite('ProtocolServerHandler', () => {
|
||||
const resp = findResponse(transport.sent, 1);
|
||||
assert.ok(resp);
|
||||
const result = (resp as { result: IInitializeResult }).result;
|
||||
assert.strictEqual(URI.revive(result.defaultDirectory!).fsPath, '/home/testuser');
|
||||
assert.strictEqual(URI.revive(result.defaultDirectory!).path, '/home/testuser');
|
||||
});
|
||||
|
||||
test('browseDirectory routes to side effect handler', async () => {
|
||||
@@ -355,7 +355,7 @@ suite('ProtocolServerHandler', () => {
|
||||
const resp = await responsePromise;
|
||||
|
||||
assert.strictEqual(sideEffects.browsedUris.length, 1);
|
||||
assert.strictEqual(sideEffects.browsedUris[0].fsPath, '/home/user/project');
|
||||
assert.strictEqual(sideEffects.browsedUris[0].path, '/home/user/project');
|
||||
|
||||
assert.ok(resp);
|
||||
const result = (resp as { result: { entries: { name: string; uri: unknown; type: string }[] } }).result;
|
||||
|
||||
Reference in New Issue
Block a user