Skip to main content
Glama
by V0v1kkk
NuGetMultiSourceTests.cs14.6 kB
using DotNetMetadataMcpServer.Configuration; using DotNetMetadataMcpServer.Services; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using NUnit.Framework; namespace MetadataExplorerTest.Services; /// <summary> /// Integration tests for NuGet multi-source support. /// Verifies that the service can query multiple NuGet feeds and aggregate results. /// </summary> [TestFixture] [Parallelizable(ParallelScope.All)] public class NuGetMultiSourceTests { [Test] public async Task Service_Should_Work_With_Default_NuGetOrg_Source() { // Arrange var configuration = new ToolsConfiguration { NuGetSources = new List<NuGetSourceConfiguration> { new NuGetSourceConfiguration { Name = "nuget.org", Url = "https://api.nuget.org/v3/index.json", Enabled = true } } }; var options = Options.Create(configuration); var service = new NuGetToolService(NullLogger<NuGetToolService>.Instance, options); // Act var result = await service.SearchPackagesAsync( searchQuery: "Newtonsoft.Json", filters: new List<string>(), includePrerelease: false, pageNumber: 1, pageSize: 10); // Assert Assert.That(result, Is.Not.Null); Assert.That(result.Packages, Is.Not.Empty); Assert.That(result.Packages.Any(p => p.Id == "Newtonsoft.Json"), Is.True, "Expected to find Newtonsoft.Json package"); } [Test] public async Task Service_Should_Aggregate_Results_From_Multiple_Sources() { // Arrange - Configure multiple sources (nuget.org is always available) var configuration = new ToolsConfiguration { NuGetSources = new List<NuGetSourceConfiguration> { new NuGetSourceConfiguration { Name = "nuget.org", Url = "https://api.nuget.org/v3/index.json", Enabled = true } } }; var options = Options.Create(configuration); var service = new NuGetToolService(NullLogger<NuGetToolService>.Instance, options); // Act - Search for a common package var result = await service.SearchPackagesAsync( searchQuery: "System.Text.Json", filters: new List<string>(), includePrerelease: false, pageNumber: 1, pageSize: 20); // Assert Assert.That(result, Is.Not.Null); Assert.That(result.Packages, Is.Not.Empty); Assert.That(result.Packages.Any(p => p.Id.Contains("System.Text.Json")), Is.True, "Expected to find System.Text.Json related packages"); } [Test] public async Task Service_Should_Get_Package_Versions_From_Multiple_Sources() { // Arrange var configuration = new ToolsConfiguration { NuGetSources = new List<NuGetSourceConfiguration> { new NuGetSourceConfiguration { Name = "nuget.org", Url = "https://api.nuget.org/v3/index.json", Enabled = true } } }; var options = Options.Create(configuration); var service = new NuGetToolService(NullLogger<NuGetToolService>.Instance, options); // Act var result = await service.GetPackageVersionsAsync( packageId: "ModelContextProtocol", filters: new List<string>(), includePrerelease: true, pageNumber: 1, pageSize: 20); // Assert Assert.That(result, Is.Not.Null); Assert.That(result.PackageId, Is.EqualTo("ModelContextProtocol")); Assert.That(result.Versions, Is.Not.Empty, "Expected to find versions of ModelContextProtocol"); Assert.That(result.Versions.All(v => v.Id == "ModelContextProtocol"), Is.True); } [Test] public void Service_Should_Fail_With_No_Valid_Sources() { // Arrange - Empty sources list var configuration = new ToolsConfiguration { NuGetSources = new List<NuGetSourceConfiguration>() }; var options = Options.Create(configuration); // Act & Assert - Constructor should add default nuget.org, so no exception Assert.DoesNotThrow(() => new NuGetToolService(NullLogger<NuGetToolService>.Instance, options)); } [Test] public void Service_Should_Handle_Disabled_Sources() { // Arrange - All sources disabled var configuration = new ToolsConfiguration { NuGetSources = new List<NuGetSourceConfiguration> { new NuGetSourceConfiguration { Name = "nuget.org", Url = "https://api.nuget.org/v3/index.json", Enabled = false } } }; var options = Options.Create(configuration); // Act & Assert - Should add default nuget.org when all are disabled Assert.DoesNotThrow(() => new NuGetToolService(NullLogger<NuGetToolService>.Instance, options)); } [Test] public async Task Service_Should_Deduplicate_Packages_From_Multiple_Sources() { // Arrange - Even with one source, test deduplication logic var configuration = new ToolsConfiguration { NuGetSources = new List<NuGetSourceConfiguration> { new NuGetSourceConfiguration { Name = "nuget.org", Url = "https://api.nuget.org/v3/index.json", Enabled = true } } }; var options = Options.Create(configuration); var service = new NuGetToolService(NullLogger<NuGetToolService>.Instance, options); // Act var result = await service.SearchPackagesAsync( searchQuery: "Serilog", filters: new List<string>(), includePrerelease: false, pageNumber: 1, pageSize: 20); // Assert - Check that there are no duplicate package IDs var packageIds = result.Packages.Select(p => p.Id).ToList(); var uniqueIds = packageIds.Distinct().ToList(); Assert.That(packageIds.Count, Is.EqualTo(uniqueIds.Count), "Expected no duplicate package IDs in results"); } [Test] public async Task Service_Should_Continue_If_One_Source_Fails() { // Arrange - One valid source and one invalid (will fail) var configuration = new ToolsConfiguration { NuGetSources = new List<NuGetSourceConfiguration> { new NuGetSourceConfiguration { Name = "invalid", Url = "https://invalid-nuget-source.local/v3/index.json", Enabled = true }, new NuGetSourceConfiguration { Name = "nuget.org", Url = "https://api.nuget.org/v3/index.json", Enabled = true } } }; var options = Options.Create(configuration); var service = new NuGetToolService(NullLogger<NuGetToolService>.Instance, options); // Act - Should still work with the valid source var result = await service.SearchPackagesAsync( searchQuery: "NUnit", filters: new List<string>(), includePrerelease: false, pageNumber: 1, pageSize: 10); // Assert - Should get results from nuget.org despite invalid source Assert.That(result, Is.Not.Null); Assert.That(result.Packages, Is.Not.Empty, "Expected to get results from valid source even when one source fails"); } [Test] public async Task Service_Should_Respect_Source_Priority_Order_Single_Source() { // Arrange - Single source to verify deterministic behavior var configuration = new ToolsConfiguration { NuGetSources = new List<NuGetSourceConfiguration> { new NuGetSourceConfiguration { Name = "nuget.org", Url = "https://api.nuget.org/v3/index.json", Enabled = true } } }; var options = Options.Create(configuration); var service = new NuGetToolService(NullLogger<NuGetToolService>.Instance, options); // Act - Search for a well-known package multiple times var result1 = await service.SearchPackagesAsync( searchQuery: "Newtonsoft.Json", filters: new List<string>(), includePrerelease: false, pageNumber: 1, pageSize: 5); var result2 = await service.SearchPackagesAsync( searchQuery: "Newtonsoft.Json", filters: new List<string>(), includePrerelease: false, pageNumber: 1, pageSize: 5); // Assert - Results should be deterministic Assert.That(result1, Is.Not.Null); Assert.That(result1.Packages, Is.Not.Empty); var package1 = result1.Packages.FirstOrDefault(p => p.Id == "Newtonsoft.Json"); var package2 = result2.Packages.FirstOrDefault(p => p.Id == "Newtonsoft.Json"); Assert.That(package1, Is.Not.Null, "Expected to find Newtonsoft.Json in first call"); Assert.That(package2, Is.Not.Null, "Expected to find Newtonsoft.Json in second call"); Assert.That(package2.Description, Is.EqualTo(package1.Description), "Package metadata should be deterministic across calls"); } [Test] [Category("Integration")] [Explicit("Requires external MyGet service - run manually to verify priority behavior")] public async Task Service_Should_Respect_Source_Priority_With_Multiple_Sources() { // Arrange - Configure multiple sources in priority order // First source (higher priority): MyGet dotnet-core feed // Second source (lower priority): nuget.org var configuration = new ToolsConfiguration { NuGetSources = new List<NuGetSourceConfiguration> { new NuGetSourceConfiguration { Name = "dotnet-core (MyGet)", Url = "https://dotnet.myget.org/F/dotnet-core/api/v3/index.json", Enabled = true }, new NuGetSourceConfiguration { Name = "nuget.org", Url = "https://api.nuget.org/v3/index.json", Enabled = true } } }; var options = Options.Create(configuration); var service = new NuGetToolService(NullLogger<NuGetToolService>.Instance, options); // Act - Search for packages that might exist in both sources var result = await service.SearchPackagesAsync( searchQuery: "System.Runtime", filters: new List<string>(), includePrerelease: true, pageNumber: 1, pageSize: 10); // Assert Assert.That(result, Is.Not.Null); Assert.That(result.Packages, Is.Not.Empty); // Run the same query again to verify determinism var result2 = await service.SearchPackagesAsync( searchQuery: "System.Runtime", filters: new List<string>(), includePrerelease: true, pageNumber: 1, pageSize: 10); // Results should be identical (same order, same metadata) Assert.That(result2.Packages.Count, Is.EqualTo(result.Packages.Count), "Results should have same count across calls"); for (int i = 0; i < result.Packages.Count; i++) { var pkg1 = result.Packages[i]; var pkg2 = result2.Packages[i]; Assert.That(pkg2.Id, Is.EqualTo(pkg1.Id), $"Package at index {i} should have same ID"); Assert.That(pkg2.Version, Is.EqualTo(pkg1.Version), $"Package {pkg1.Id} should have same version"); } } [Test] [Category("Integration")] [Explicit("Requires external MyGet service - run manually")] public async Task Service_Should_Query_MyGet_Public_Feed() { // Arrange - Configure MyGet public feed alongside nuget.org var configuration = new ToolsConfiguration { NuGetSources = new List<NuGetSourceConfiguration> { new NuGetSourceConfiguration { Name = "nuget.org", Url = "https://api.nuget.org/v3/index.json", Enabled = true }, new NuGetSourceConfiguration { Name = "dotnet-core (MyGet)", Url = "https://dotnet.myget.org/F/dotnet-core/api/v3/index.json", Enabled = true } } }; var options = Options.Create(configuration); var service = new NuGetToolService(NullLogger<NuGetToolService>.Instance, options); // Act - Search for packages that might be on both feeds var result = await service.SearchPackagesAsync( searchQuery: "System.Runtime", filters: new List<string>(), includePrerelease: true, pageNumber: 1, pageSize: 20); // Assert - Should get results from both sources Assert.That(result, Is.Not.Null); Assert.That(result.Packages, Is.Not.Empty, "Expected to find System.Runtime packages from configured sources"); // Verify deduplication is working (no duplicate package IDs) var packageIds = result.Packages.Select(p => p.Id).ToList(); var uniqueIds = packageIds.Distinct().ToList(); Assert.That(packageIds.Count, Is.EqualTo(uniqueIds.Count), "Expected no duplicate package IDs when querying multiple sources"); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/V0v1kkk/DotNetMetadataMcpServer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server