Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/mcp/test/vscode-node/nuget.stub.spec.ts
13405 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 { beforeEach, describe, expect, it } from 'vitest';
7
import { ILogService } from '../../../../platform/log/common/logService';
8
import { ITestingServicesAccessor, TestingServiceCollection } from '../../../../platform/test/node/services';
9
import { createExtensionUnitTestingServices } from '../../../test/node/services';
10
import { IInstallableMcpServer, mapServerJsonToMcpServer, McpServerType, NuGetMcpSetup, RegistryType } from '../../vscode-node/nuget';
11
import { FixtureCommandExecutor, FixtureFetcherService } from './util';
12
13
describe('get nuget MCP server info using fake CLI', { timeout: 30_000 }, () => {
14
let testingServiceCollection: TestingServiceCollection;
15
let accessor: ITestingServicesAccessor;
16
let logService: ILogService;
17
let fetcherService: FixtureFetcherService;
18
let commandExecutor: FixtureCommandExecutor;
19
let nuget: NuGetMcpSetup;
20
21
beforeEach(() => {
22
testingServiceCollection = createExtensionUnitTestingServices();
23
accessor = testingServiceCollection.createTestingAccessor();
24
logService = accessor.get(ILogService);
25
fetcherService = new FixtureFetcherService(new Map([
26
['https://api.nuget.org/v3/index.json', { fileName: 'nuget-service-index.json', status: 200 }],
27
['https://api.nuget.org/v3-flatcontainer/basetestpackage.dotnettool/1.0.0/readme', { fileName: 'nuget-readme.md', status: 200 }],
28
]));
29
commandExecutor = new FixtureCommandExecutor(new Map([
30
['dotnet --version', { stdout: '10.0.100-preview.7.25358.102', exitCode: 0 }]
31
]));
32
nuget = new NuGetMcpSetup(logService, fetcherService, commandExecutor);
33
});
34
35
it('converts legacy schema version', async () => {
36
const manifest = {
37
'$schema': 'https://modelcontextprotocol.io/schemas/draft/2025-07-09/server.json',
38
packages: [{ registry_name: 'nuget', name: 'MismatchId', version: '0.1.0' }]
39
};
40
const expected = {
41
name: 'CorrectId',
42
version: '0.2.0',
43
description: 'CorrectId',
44
'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json',
45
packages: [{ registry_name: 'nuget', name: 'CorrectId', version: '0.2.0' }]
46
};
47
48
const actual = nuget.prepareServerJson(manifest, 'CorrectId', '0.2.0');
49
50
expect(actual).toEqual(expected);
51
});
52
53
it('handles original 2025-07-09 schema version', async () => {
54
const manifest = {
55
'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json',
56
packages: [{ registry_name: 'nuget', name: 'MismatchId', version: '0.1.0' }]
57
};
58
const expected = {
59
'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json',
60
name: 'CorrectId',
61
version: '0.2.0',
62
description: 'CorrectId',
63
packages: [{ registry_name: 'nuget', name: 'CorrectId', version: '0.2.0' }],
64
};
65
66
const actual = nuget.prepareServerJson(manifest, 'CorrectId', '0.2.0');
67
68
expect(actual).toEqual(expected);
69
});
70
71
it('handles latest 2025-07-09 schema version', async () => {
72
const manifest = {
73
'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json',
74
packages: [{ registry_type: 'nuget', name: 'MismatchId', version: '0.1.0' }]
75
};
76
const expected = {
77
'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json',
78
name: 'CorrectId',
79
version: '0.2.0',
80
description: 'CorrectId',
81
packages: [{ registry_type: 'nuget', name: 'CorrectId', version: '0.2.0' }],
82
};
83
84
const actual = nuget.prepareServerJson(manifest, 'CorrectId', '0.2.0');
85
86
expect(actual).toEqual(expected);
87
});
88
89
it('handles latest 2025-09-29 schema version', async () => {
90
const manifest = {
91
'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json',
92
packages: [{ registryType: 'nuget', name: 'MismatchId', version: '0.1.0' }]
93
};
94
const expected = {
95
'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json',
96
name: 'CorrectId',
97
version: '0.2.0',
98
description: 'CorrectId',
99
packages: [{ registryType: 'nuget', name: 'CorrectId', version: '0.2.0' }],
100
};
101
102
const actual = nuget.prepareServerJson(manifest, 'CorrectId', '0.2.0');
103
104
expect(actual).toEqual(expected);
105
});
106
107
it('returns package metadata', async () => {
108
commandExecutor.fullCommandToResultMap.set(
109
'dotnet package search basetestpackage.DOTNETTOOL --source https://api.nuget.org/v3/index.json --prerelease --format json',
110
{ fileName: 'dotnet-package-search-exists.json', exitCode: 0 });
111
const result = await nuget.getNuGetPackageMetadata('basetestpackage.DOTNETTOOL');
112
expect(result.state).toBe('ok');
113
if (result.state === 'ok') {
114
expect(result.name).toBe('BaseTestPackage.DotnetTool');
115
expect(result.version).toBe('1.0.0');
116
expect(result.publisher).toBe('NuGetTestData');
117
await expect(result.readme).toMatchFileSnapshot('fixtures/snapshots/nuget-readme.md');
118
} else {
119
expect.fail();
120
}
121
});
122
123
it('handles missing package', async () => {
124
commandExecutor.fullCommandToResultMap.set(
125
'dotnet package search basetestpackage.dotnettool --source https://api.nuget.org/v3/index.json --prerelease --format json',
126
{ fileName: 'dotnet-package-search-does-not-exist.json', exitCode: 0 });
127
const result = await nuget.getNuGetPackageMetadata('basetestpackage.dotnettool');
128
expect(result.state).toBe('error');
129
if (result.state === 'error') {
130
expect(result.error).toBeDefined();
131
expect(result.errorType).toBe('NotFound');
132
} else {
133
expect.fail();
134
}
135
});
136
});
137
138
describe('mapServerJsonToMcpServer', () => {
139
it('handles 2025-07-09 schema version', async () => {
140
const manifest = {
141
'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json',
142
name: 'test',
143
description: 'test',
144
version: '1.0.0',
145
packages: [{ registry_type: 'nuget', name: 'SomeId', version: '0.1.0' }]
146
};
147
const expected: Omit<IInstallableMcpServer, 'name'> = {
148
config: {
149
type: McpServerType.LOCAL,
150
command: 'dnx',
151
args: ['[email protected]', '--yes']
152
}
153
};
154
155
const actual = mapServerJsonToMcpServer(manifest, RegistryType.NUGET);
156
157
expect(actual).toEqual(expected);
158
});
159
160
it('handles 2025-09-29 schema version', async () => {
161
const manifest = {
162
'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json',
163
name: 'test',
164
description: 'test',
165
version: '1.0.0',
166
packages: [{ registryType: 'nuget', identifier: 'SomeId', version: '0.1.0' }]
167
};
168
const expected: Omit<IInstallableMcpServer, 'name'> = {
169
config: {
170
type: McpServerType.LOCAL,
171
command: 'dnx',
172
args: ['[email protected]', '--yes']
173
}
174
};
175
176
const actual = mapServerJsonToMcpServer(manifest, RegistryType.NUGET);
177
178
expect(actual).toEqual(expected);
179
});
180
181
it('defaults to first package without matching type', async () => {
182
const manifest = {
183
'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json',
184
name: 'test',
185
description: 'test',
186
version: '1.0.0',
187
packages: [{ registryType: 'npm', identifier: 'SomeId', version: '0.1.0' }]
188
};
189
const expected: Omit<IInstallableMcpServer, 'name'> = {
190
config: {
191
type: McpServerType.LOCAL,
192
command: 'npx',
193
args: ['[email protected]']
194
}
195
};
196
197
const actual = mapServerJsonToMcpServer(manifest, RegistryType.NUGET);
198
199
expect(actual).toEqual(expected);
200
});
201
});
202
203