Path: blob/main/extensions/copilot/src/extension/mcp/test/vscode-node/nuget.stub.spec.ts
13405 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { beforeEach, describe, expect, it } from 'vitest';6import { ILogService } from '../../../../platform/log/common/logService';7import { ITestingServicesAccessor, TestingServiceCollection } from '../../../../platform/test/node/services';8import { createExtensionUnitTestingServices } from '../../../test/node/services';9import { IInstallableMcpServer, mapServerJsonToMcpServer, McpServerType, NuGetMcpSetup, RegistryType } from '../../vscode-node/nuget';10import { FixtureCommandExecutor, FixtureFetcherService } from './util';1112describe('get nuget MCP server info using fake CLI', { timeout: 30_000 }, () => {13let testingServiceCollection: TestingServiceCollection;14let accessor: ITestingServicesAccessor;15let logService: ILogService;16let fetcherService: FixtureFetcherService;17let commandExecutor: FixtureCommandExecutor;18let nuget: NuGetMcpSetup;1920beforeEach(() => {21testingServiceCollection = createExtensionUnitTestingServices();22accessor = testingServiceCollection.createTestingAccessor();23logService = accessor.get(ILogService);24fetcherService = new FixtureFetcherService(new Map([25['https://api.nuget.org/v3/index.json', { fileName: 'nuget-service-index.json', status: 200 }],26['https://api.nuget.org/v3-flatcontainer/basetestpackage.dotnettool/1.0.0/readme', { fileName: 'nuget-readme.md', status: 200 }],27]));28commandExecutor = new FixtureCommandExecutor(new Map([29['dotnet --version', { stdout: '10.0.100-preview.7.25358.102', exitCode: 0 }]30]));31nuget = new NuGetMcpSetup(logService, fetcherService, commandExecutor);32});3334it('converts legacy schema version', async () => {35const manifest = {36'$schema': 'https://modelcontextprotocol.io/schemas/draft/2025-07-09/server.json',37packages: [{ registry_name: 'nuget', name: 'MismatchId', version: '0.1.0' }]38};39const expected = {40name: 'CorrectId',41version: '0.2.0',42description: 'CorrectId',43'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json',44packages: [{ registry_name: 'nuget', name: 'CorrectId', version: '0.2.0' }]45};4647const actual = nuget.prepareServerJson(manifest, 'CorrectId', '0.2.0');4849expect(actual).toEqual(expected);50});5152it('handles original 2025-07-09 schema version', async () => {53const manifest = {54'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json',55packages: [{ registry_name: 'nuget', name: 'MismatchId', version: '0.1.0' }]56};57const expected = {58'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json',59name: 'CorrectId',60version: '0.2.0',61description: 'CorrectId',62packages: [{ registry_name: 'nuget', name: 'CorrectId', version: '0.2.0' }],63};6465const actual = nuget.prepareServerJson(manifest, 'CorrectId', '0.2.0');6667expect(actual).toEqual(expected);68});6970it('handles latest 2025-07-09 schema version', async () => {71const manifest = {72'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json',73packages: [{ registry_type: 'nuget', name: 'MismatchId', version: '0.1.0' }]74};75const expected = {76'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json',77name: 'CorrectId',78version: '0.2.0',79description: 'CorrectId',80packages: [{ registry_type: 'nuget', name: 'CorrectId', version: '0.2.0' }],81};8283const actual = nuget.prepareServerJson(manifest, 'CorrectId', '0.2.0');8485expect(actual).toEqual(expected);86});8788it('handles latest 2025-09-29 schema version', async () => {89const manifest = {90'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json',91packages: [{ registryType: 'nuget', name: 'MismatchId', version: '0.1.0' }]92};93const expected = {94'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json',95name: 'CorrectId',96version: '0.2.0',97description: 'CorrectId',98packages: [{ registryType: 'nuget', name: 'CorrectId', version: '0.2.0' }],99};100101const actual = nuget.prepareServerJson(manifest, 'CorrectId', '0.2.0');102103expect(actual).toEqual(expected);104});105106it('returns package metadata', async () => {107commandExecutor.fullCommandToResultMap.set(108'dotnet package search basetestpackage.DOTNETTOOL --source https://api.nuget.org/v3/index.json --prerelease --format json',109{ fileName: 'dotnet-package-search-exists.json', exitCode: 0 });110const result = await nuget.getNuGetPackageMetadata('basetestpackage.DOTNETTOOL');111expect(result.state).toBe('ok');112if (result.state === 'ok') {113expect(result.name).toBe('BaseTestPackage.DotnetTool');114expect(result.version).toBe('1.0.0');115expect(result.publisher).toBe('NuGetTestData');116await expect(result.readme).toMatchFileSnapshot('fixtures/snapshots/nuget-readme.md');117} else {118expect.fail();119}120});121122it('handles missing package', async () => {123commandExecutor.fullCommandToResultMap.set(124'dotnet package search basetestpackage.dotnettool --source https://api.nuget.org/v3/index.json --prerelease --format json',125{ fileName: 'dotnet-package-search-does-not-exist.json', exitCode: 0 });126const result = await nuget.getNuGetPackageMetadata('basetestpackage.dotnettool');127expect(result.state).toBe('error');128if (result.state === 'error') {129expect(result.error).toBeDefined();130expect(result.errorType).toBe('NotFound');131} else {132expect.fail();133}134});135});136137describe('mapServerJsonToMcpServer', () => {138it('handles 2025-07-09 schema version', async () => {139const manifest = {140'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json',141name: 'test',142description: 'test',143version: '1.0.0',144packages: [{ registry_type: 'nuget', name: 'SomeId', version: '0.1.0' }]145};146const expected: Omit<IInstallableMcpServer, 'name'> = {147config: {148type: McpServerType.LOCAL,149command: 'dnx',150args: ['[email protected]', '--yes']151}152};153154const actual = mapServerJsonToMcpServer(manifest, RegistryType.NUGET);155156expect(actual).toEqual(expected);157});158159it('handles 2025-09-29 schema version', async () => {160const manifest = {161'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json',162name: 'test',163description: 'test',164version: '1.0.0',165packages: [{ registryType: 'nuget', identifier: 'SomeId', version: '0.1.0' }]166};167const expected: Omit<IInstallableMcpServer, 'name'> = {168config: {169type: McpServerType.LOCAL,170command: 'dnx',171args: ['[email protected]', '--yes']172}173};174175const actual = mapServerJsonToMcpServer(manifest, RegistryType.NUGET);176177expect(actual).toEqual(expected);178});179180it('defaults to first package without matching type', async () => {181const manifest = {182'$schema': 'https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json',183name: 'test',184description: 'test',185version: '1.0.0',186packages: [{ registryType: 'npm', identifier: 'SomeId', version: '0.1.0' }]187};188const expected: Omit<IInstallableMcpServer, 'name'> = {189config: {190type: McpServerType.LOCAL,191command: 'npx',192args: ['[email protected]']193}194};195196const actual = mapServerJsonToMcpServer(manifest, RegistryType.NUGET);197198expect(actual).toEqual(expected);199});200});201202203