Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/api/test/node/extHostHooks.test.ts
5240 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import { URI } from '../../../../base/common/uri.js';
8
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
9
import { NullLogService } from '../../../../platform/log/common/log.js';
10
import { NodeExtHostHooks } from '../../node/extHostHooksNode.js';
11
import { IHookCommandDto, MainThreadHooksShape } from '../../common/extHost.protocol.js';
12
import { HookCommandResultKind } from '../../../contrib/chat/common/hooks/hooksCommandTypes.js';
13
import { IHookResult } from '../../../contrib/chat/common/hooks/hooksTypes.js';
14
import { IExtHostRpcService } from '../../common/extHostRpcService.js';
15
import { CancellationToken } from '../../../../base/common/cancellation.js';
16
17
function createHookCommandDto(command: string, options?: Partial<Omit<IHookCommandDto, 'type' | 'command'>>): IHookCommandDto {
18
return {
19
type: 'command',
20
command,
21
...options,
22
};
23
}
24
25
function createMockExtHostRpcService(mainThreadProxy: MainThreadHooksShape): IExtHostRpcService {
26
return {
27
_serviceBrand: undefined,
28
getProxy<T>(): T {
29
return mainThreadProxy as unknown as T;
30
},
31
set<T, R extends T>(_identifier: unknown, instance: R): R {
32
return instance;
33
},
34
dispose(): void { },
35
assertRegistered(): void { },
36
drain(): Promise<void> { return Promise.resolve(); },
37
} as IExtHostRpcService;
38
}
39
40
suite.skip('ExtHostHooks', () => {
41
ensureNoDisposablesAreLeakedInTestSuite();
42
43
let hooksService: NodeExtHostHooks;
44
45
setup(() => {
46
const mockMainThreadProxy: MainThreadHooksShape = {
47
$executeHook: async (): Promise<IHookResult[]> => {
48
return [];
49
},
50
dispose: () => { }
51
};
52
53
const mockRpcService = createMockExtHostRpcService(mockMainThreadProxy);
54
hooksService = new NodeExtHostHooks(mockRpcService, new NullLogService());
55
});
56
57
test('$runHookCommand runs command and returns success result', async () => {
58
const hookCommand = createHookCommandDto('echo "hello world"');
59
const result = await hooksService.$runHookCommand(hookCommand, undefined, CancellationToken.None);
60
61
assert.strictEqual(result.kind, HookCommandResultKind.Success);
62
assert.strictEqual((result.result as string).trim(), 'hello world');
63
});
64
65
test('$runHookCommand parses JSON output', async () => {
66
const hookCommand = createHookCommandDto('echo \'{"key": "value"}\'');
67
const result = await hooksService.$runHookCommand(hookCommand, undefined, CancellationToken.None);
68
69
assert.strictEqual(result.kind, HookCommandResultKind.Success);
70
assert.deepStrictEqual(result.result, { key: 'value' });
71
});
72
73
test('$runHookCommand returns non-blocking error for exit code 1', async () => {
74
const hookCommand = createHookCommandDto('exit 1');
75
const result = await hooksService.$runHookCommand(hookCommand, undefined, CancellationToken.None);
76
77
assert.strictEqual(result.kind, HookCommandResultKind.NonBlockingError);
78
});
79
80
test('$runHookCommand returns blocking error for exit code 2', async () => {
81
const hookCommand = createHookCommandDto('exit 2');
82
const result = await hooksService.$runHookCommand(hookCommand, undefined, CancellationToken.None);
83
84
assert.strictEqual(result.kind, HookCommandResultKind.Error);
85
});
86
87
test('$runHookCommand captures stderr on non-blocking error', async () => {
88
const hookCommand = createHookCommandDto('echo "error message" >&2 && exit 1');
89
const result = await hooksService.$runHookCommand(hookCommand, undefined, CancellationToken.None);
90
91
assert.strictEqual(result.kind, HookCommandResultKind.NonBlockingError);
92
assert.strictEqual((result.result as string).trim(), 'error message');
93
});
94
95
test('$runHookCommand captures stderr on blocking error', async () => {
96
const hookCommand = createHookCommandDto('echo "blocking error" >&2 && exit 2');
97
const result = await hooksService.$runHookCommand(hookCommand, undefined, CancellationToken.None);
98
99
assert.strictEqual(result.kind, HookCommandResultKind.Error);
100
assert.strictEqual((result.result as string).trim(), 'blocking error');
101
});
102
103
test('$runHookCommand passes input to stdin as JSON', async () => {
104
const hookCommand = createHookCommandDto('cat');
105
const input = { tool: 'bash', args: { command: 'ls' } };
106
const result = await hooksService.$runHookCommand(hookCommand, input, CancellationToken.None);
107
108
assert.strictEqual(result.kind, HookCommandResultKind.Success);
109
assert.deepStrictEqual(result.result, input);
110
});
111
112
test('$runHookCommand returns non-blocking error for invalid command', async () => {
113
const hookCommand = createHookCommandDto('/nonexistent/command/that/does/not/exist');
114
const result = await hooksService.$runHookCommand(hookCommand, undefined, CancellationToken.None);
115
116
// Invalid commands typically return non-zero exit codes (127 for command not found)
117
// which are treated as non-blocking errors unless it's exit code 2
118
assert.strictEqual(result.kind, HookCommandResultKind.NonBlockingError);
119
});
120
121
test('$runHookCommand uses custom environment variables', async () => {
122
const hookCommand = createHookCommandDto('echo $MY_VAR', { env: { MY_VAR: 'custom_value' } });
123
const result = await hooksService.$runHookCommand(hookCommand, undefined, CancellationToken.None);
124
125
assert.strictEqual(result.kind, HookCommandResultKind.Success);
126
assert.strictEqual((result.result as string).trim(), 'custom_value');
127
});
128
129
test('$runHookCommand uses custom cwd', async () => {
130
const hookCommand = createHookCommandDto('pwd', { cwd: URI.file('/tmp') });
131
const result = await hooksService.$runHookCommand(hookCommand, undefined, CancellationToken.None);
132
133
assert.strictEqual(result.kind, HookCommandResultKind.Success);
134
// The result should contain /tmp or /private/tmp (macOS symlink)
135
assert.ok((result.result as string).includes('tmp'));
136
});
137
});
138
139