From ca27961dca926709773fd9c702305fb9186bd666 Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Mon, 17 Nov 2025 16:04:53 -0800 Subject: [PATCH 01/10] Factorize PackageDwnloadRunner --- .../Package/Download/PackageDownloadRunner.cs | 88 +++++++++++-------- .../Download/PackageDownloadRunnerTests.cs | 12 +++ 2 files changed, 65 insertions(+), 35 deletions(-) diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs index 940821c4ad3..63e8dfc12e9 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs @@ -238,53 +238,71 @@ private static bool TryGetRepositoriesForPackage( out IReadOnlyList repositories) { var mappedNames = packageSourceMapping.GetConfiguredPackageSources(packageId); + IReadOnlyList resultRepositories; - // Only validate insecure sources when mapping produced something if (mappedNames.Count > 0) { - var mappedRepos = new List(mappedNames.Count); - foreach (var mappedName in mappedNames) - { - SourceRepository? repo = null; - for (int i = 0; i < allRepos.Count; i++) - { - if (string.Equals(allRepos[i].PackageSource.Name, mappedName, StringComparison.OrdinalIgnoreCase)) - { - repo = allRepos[i]; - break; - } - } + resultRepositories = GetMappedRepositories(mappedNames, allRepos, packageId, logger); - if (repo != null) - { - mappedRepos.Add(repo); - } - else - { - logger.LogVerbose( - string.Format( - CultureInfo.CurrentCulture, - Strings.PackageDownloadCommand_PackageSourceMapping_NoSuchSource, - mappedName, - packageId)); - } - } - - if (DetectAndReportInsecureSources(args.AllowInsecureConnections, mappedRepos.Select(repo => repo.PackageSource), logger)) + if (DetectAndReportInsecureSources(args.AllowInsecureConnections, resultRepositories.Select(repo => repo.PackageSource), logger)) { repositories = []; return false; } - - repositories = mappedRepos; - return true; } else { - // No mapping for this package: fall back to all sources - repositories = allRepos; - return true; + // No mapping for this package + resultRepositories = allRepos; } + + repositories = resultRepositories; + return true; + } + + private static IReadOnlyList GetMappedRepositories( + IReadOnlyList mappedNames, + IReadOnlyList allRepos, + string packageId, + ILoggerWithColor logger) + { + var mappedRepos = new List(mappedNames.Count); + + foreach (var mappedName in mappedNames) + { + SourceRepository? repo = FindRepositoryByName(mappedName, allRepos); + + if (repo != null) + { + mappedRepos.Add(repo); + } + else + { + logger.LogVerbose( + string.Format( + CultureInfo.CurrentCulture, + Strings.PackageDownloadCommand_PackageSourceMapping_NoSuchSource, + mappedName, + packageId)); + } + } + + return mappedRepos; + } + + private static SourceRepository? FindRepositoryByName( + string mappedName, + IReadOnlyList allRepos) + { + for (int i = 0; i < allRepos.Count; i++) + { + if (string.Equals(allRepos[i].PackageSource.Name, mappedName, StringComparison.OrdinalIgnoreCase)) + { + return allRepos[i]; + } + } + + return null; } private static async Task DownloadPackageAsync( diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs index d96dcc6559f..ba948dd7efe 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs @@ -472,6 +472,18 @@ public static IEnumerable Cases() false, null! }; + + // no --source, mapping -> A&B, package in both A and B. Latest version from A installed + yield return new object[] + { + new List<(string,string)> { ("Contoso.Mapped", "3.0.0") }, // A + new List<(string,string)> { ("Contoso.Mapped", "2.0.0") }, // B + new List<(string,string)> { ("A", "Contoso.*"), ("B", "Contoso.*") }, // mapped to A&B + null, + "Contoso.Mapped", null, + true, + ("Contoso.Mapped", "3.0.0") + }; } [Theory] From 6d017d886531b6d2490a48ba1a9b70e6abef223a Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Wed, 19 Nov 2025 10:24:19 -0800 Subject: [PATCH 02/10] insecure validation --- .../Package/Download/PackageDownloadRunner.cs | 52 ++--------- .../Download/PackageDownloadRunnerTests.cs | 92 ++++++++++++++++++- 2 files changed, 95 insertions(+), 49 deletions(-) diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs index 63e8dfc12e9..4ef63770311 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs @@ -91,15 +91,15 @@ public static async Task RunAsync(PackageDownloadArgs args, ILoggerWithColo } else { - if (!TryGetRepositoriesForPackage( - package.Id, - args, - packageSourceMapping!, - allRepositories, - logger, - out sourceRepositories)) + var mappedNames = packageSourceMapping!.GetConfiguredPackageSources(package.Id); + sourceRepositories = mappedNames.Count > 0 + ? GetMappedRepositories(mappedNames, allRepositories, package.Id, logger) + : allRepositories; + + if (DetectAndReportInsecureSources(args.AllowInsecureConnections, sourceRepositories.Select(r => r.PackageSource), logger)) { - return ExitCodeError; + downloadedAllSuccessfully &= false; + continue; } } @@ -224,42 +224,6 @@ await ResolvePackageDownloadVersion( return (versionToDownload, downloadSourceRepository); } - /// - /// Builds the set of SourceRepository objects to use for a given package, - /// applying package source mapping - /// validating HTTP usage only on the *effective* sources. - /// - private static bool TryGetRepositoriesForPackage( - string packageId, - PackageDownloadArgs args, - PackageSourceMapping packageSourceMapping, - IReadOnlyList allRepos, - ILoggerWithColor logger, - out IReadOnlyList repositories) - { - var mappedNames = packageSourceMapping.GetConfiguredPackageSources(packageId); - IReadOnlyList resultRepositories; - - if (mappedNames.Count > 0) - { - resultRepositories = GetMappedRepositories(mappedNames, allRepos, packageId, logger); - - if (DetectAndReportInsecureSources(args.AllowInsecureConnections, resultRepositories.Select(repo => repo.PackageSource), logger)) - { - repositories = []; - return false; - } - } - else - { - // No mapping for this package - resultRepositories = allRepos; - } - - repositories = resultRepositories; - return true; - } - private static IReadOnlyList GetMappedRepositories( IReadOnlyList mappedNames, IReadOnlyList allRepos, diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs index ba948dd7efe..4f4a084376b 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs @@ -582,10 +582,9 @@ public async Task RunAsync_WhenSourceMappingMapsToInsecureSource_AndAllowInsecur { // Arrange using var context = new SimpleTestPathContext(); - var insecureSourceUrl = "http://contoso.test/v3/index.json"; - - context.Settings.AddSource("InsecureMapped", insecureSourceUrl); - context.Settings.AddPackageSourceMapping("InsecureMapped", "Contoso.*"); + PackageSource source = new("http://contoso.test/v3/index.json", "InsecureMapped"); + context.Settings.AddSource(source.Name, source.SourceUri.OriginalString); + context.Settings.AddPackageSourceMapping(source.Name, "Contoso.*"); var settings = Settings.LoadSettingsGivenConfigPaths([context.Settings.ConfigPath]); @@ -606,7 +605,7 @@ public async Task RunAsync_WhenSourceMappingMapsToInsecureSource_AndAllowInsecur var logger = new Mock(MockBehavior.Loose); var packageSources = new List { - new(insecureSourceUrl, "InsecureMapped") + source }; // Act @@ -619,6 +618,15 @@ public async Task RunAsync_WhenSourceMappingMapsToInsecureSource_AndAllowInsecur // Assert exit.Should().Be(PackageDownloadRunner.ExitCodeError); + string expectedError = string.Format( + CultureInfo.CurrentCulture, + Strings.Error_HttpServerUsage, + "package download", + source); + logger.Verify( + l => l.LogError(expectedError), + Times.AtLeastOnce); + } [Fact] @@ -675,6 +683,80 @@ public async Task RunAsync_WhenSourceMappingMapsToSecureLocalSource_DoesNotLogEr File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{version}.nupkg")).Should().BeTrue(); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task RunAsync_WhenSourceMappingEnabled_PackageMapsToNoSource(bool allowInsecureConnections) + { + // Arrange + using var context = new SimpleTestPathContext(); + string srcDirectory = Path.Combine(context.PackageSource, "HttpSource"); + using var server = new FileSystemBackedV3MockServer(srcDirectory); + PackageSource source = new(server.ServiceIndexUri, "Insecure"); + context.Settings.AddSource(source.Name, source.SourceUri.OriginalString); + context.Settings.AddPackageSourceMapping(source.Name, "Contoso.Not.*"); + var settings = Settings.LoadSettingsGivenConfigPaths([context.Settings.ConfigPath]); + + // package in source + var id = "Contoso.Package"; + var version = "1.0.0"; + await SimpleTestPackageUtility.CreateFullPackageAsync(srcDirectory, id, version); + + // arguments + var logger = new Mock(MockBehavior.Loose); + var packageSources = new List + { + source + }; + var args = new PackageDownloadArgs + { + Packages = + [ + new PackageWithNuGetVersion + { + Id = id, + NuGetVersion = NuGetVersion.Parse(version) + } + ], + OutputDirectory = context.WorkingDirectory, + AllowInsecureConnections = allowInsecureConnections, + }; + + if (allowInsecureConnections) + { + server.Start(); + } + + // Act + var exit = await PackageDownloadRunner.RunAsync( + args, + logger.Object, + packageSources, + settings, + CancellationToken.None); + + // Assert + if (allowInsecureConnections) + { + exit.Should().Be(PackageDownloadRunner.ExitCodeSuccess); + var installDir = Path.Combine(context.WorkingDirectory, id.ToLowerInvariant(), version); + Directory.Exists(installDir).Should().BeTrue(); + File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{version}.nupkg")).Should().BeTrue(); + } + else + { + exit.Should().Be(PackageDownloadRunner.ExitCodeError); + string expectedError = string.Format( + CultureInfo.CurrentCulture, + Strings.Error_HttpServerUsage, + "package download", + source); + logger.Verify( + l => l.LogError(expectedError), + Times.AtLeastOnce); + } + } + [Fact] public async Task RunAsync_WhenSourceMappingMapsToInsecureSource_AndAllowInsecureConnectionsTrue_DownloadsWithoutError() { From bada15fabe7e9d2eef673e5f534b988a95b80a5f Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Wed, 19 Nov 2025 10:26:27 -0800 Subject: [PATCH 03/10] test --- .../Package/Download/PackageDownloadRunnerTests.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs index 4f4a084376b..8faa6714ab2 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs @@ -484,6 +484,18 @@ public static IEnumerable Cases() true, ("Contoso.Mapped", "3.0.0") }; + + // no --source, mapping -> A&B, package in both A and B. Latest version from B installed + yield return new object[] + { + new List<(string,string)> { ("Contoso.Mapped", "2.0.0") }, // A + new List<(string,string)> { ("Contoso.Mapped", "3.0.0") }, // B + new List<(string,string)> { ("A", "Contoso.*"), ("B", "Contoso.*") }, // mapped to A&B + null, + "Contoso.Mapped", null, + true, + ("Contoso.Mapped", "3.0.0") + }; } [Theory] From 5fed000dd5a9c1dc41182726f8dd05207be0ebdf Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Wed, 19 Nov 2025 11:04:48 -0800 Subject: [PATCH 04/10] no mockserver --- .../Download/PackageDownloadRunnerTests.cs | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs index 8faa6714ab2..4cd5238ec7e 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs @@ -702,9 +702,7 @@ public async Task RunAsync_WhenSourceMappingEnabled_PackageMapsToNoSource(bool a { // Arrange using var context = new SimpleTestPathContext(); - string srcDirectory = Path.Combine(context.PackageSource, "HttpSource"); - using var server = new FileSystemBackedV3MockServer(srcDirectory); - PackageSource source = new(server.ServiceIndexUri, "Insecure"); + PackageSource source = new("http://contoso.test/v3/index.json", "Insecure"); context.Settings.AddSource(source.Name, source.SourceUri.OriginalString); context.Settings.AddPackageSourceMapping(source.Name, "Contoso.Not.*"); var settings = Settings.LoadSettingsGivenConfigPaths([context.Settings.ConfigPath]); @@ -712,7 +710,12 @@ public async Task RunAsync_WhenSourceMappingEnabled_PackageMapsToNoSource(bool a // package in source var id = "Contoso.Package"; var version = "1.0.0"; - await SimpleTestPackageUtility.CreateFullPackageAsync(srcDirectory, id, version); + + string expectedError = string.Format( + CultureInfo.CurrentCulture, + Strings.Error_HttpServerUsage, + "package download", + source); // arguments var logger = new Mock(MockBehavior.Loose); @@ -734,11 +737,6 @@ public async Task RunAsync_WhenSourceMappingEnabled_PackageMapsToNoSource(bool a AllowInsecureConnections = allowInsecureConnections, }; - if (allowInsecureConnections) - { - server.Start(); - } - // Act var exit = await PackageDownloadRunner.RunAsync( args, @@ -750,22 +748,16 @@ public async Task RunAsync_WhenSourceMappingEnabled_PackageMapsToNoSource(bool a // Assert if (allowInsecureConnections) { - exit.Should().Be(PackageDownloadRunner.ExitCodeSuccess); - var installDir = Path.Combine(context.WorkingDirectory, id.ToLowerInvariant(), version); - Directory.Exists(installDir).Should().BeTrue(); - File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{version}.nupkg")).Should().BeTrue(); + logger.Verify( + l => l.LogError(expectedError), + Times.Never); } else { exit.Should().Be(PackageDownloadRunner.ExitCodeError); - string expectedError = string.Format( - CultureInfo.CurrentCulture, - Strings.Error_HttpServerUsage, - "package download", - source); logger.Verify( l => l.LogError(expectedError), - Times.AtLeastOnce); + Times.Once); } } From 52de77ba195db246515ba76e38b9b55cf51e7978 Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Thu, 20 Nov 2025 10:18:53 -0800 Subject: [PATCH 05/10] block not mapped source --- .../Package/Download/PackageDownloadRunner.cs | 22 ++++++++-- .../Strings.Designer.cs | 11 ++++- .../NuGet.CommandLine.XPlat/Strings.resx | 5 +++ .../xlf/Strings.cs.xlf | 6 +++ .../xlf/Strings.de.xlf | 6 +++ .../xlf/Strings.es.xlf | 6 +++ .../xlf/Strings.fr.xlf | 6 +++ .../xlf/Strings.it.xlf | 6 +++ .../xlf/Strings.ja.xlf | 6 +++ .../xlf/Strings.ko.xlf | 6 +++ .../xlf/Strings.pl.xlf | 6 +++ .../xlf/Strings.pt-BR.xlf | 6 +++ .../xlf/Strings.ru.xlf | 6 +++ .../xlf/Strings.tr.xlf | 6 +++ .../xlf/Strings.zh-Hans.xlf | 6 +++ .../xlf/Strings.zh-Hant.xlf | 6 +++ .../Download/PackageDownloadRunnerTests.cs | 41 +++++++------------ 17 files changed, 127 insertions(+), 30 deletions(-) diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs index 4ef63770311..f10ec9b8345 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs @@ -92,9 +92,25 @@ public static async Task RunAsync(PackageDownloadArgs args, ILoggerWithColo else { var mappedNames = packageSourceMapping!.GetConfiguredPackageSources(package.Id); - sourceRepositories = mappedNames.Count > 0 - ? GetMappedRepositories(mappedNames, allRepositories, package.Id, logger) - : allRepositories; + + if (mappedNames.Count == 0) + { + // fail, no sources mapped for this package + var notConsideredSources = string.Join( + ", ", + allRepositories.Select(repository => repository.PackageSource)); + + logger.LogError(string.Format( + CultureInfo.CurrentCulture, + Strings.PackageDownloadCommand_PackageSourceMapping_NoSourcesMapped, + package.Id, + notConsideredSources)); + + downloadedAllSuccessfully &= false; + continue; + } + + sourceRepositories = GetMappedRepositories(mappedNames, allRepositories, package.Id, logger); if (DetectAndReportInsecureSources(args.AllowInsecureConnections, sourceRepositories.Select(r => r.PackageSource), logger)) { diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs index a34243fa494..a0a4eb8bfae 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 @@ -1661,6 +1661,15 @@ internal static string PackageDownloadCommand_PackageIdDescription { } } + /// + /// Looks up a localized string similar to Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}.. + /// + internal static string PackageDownloadCommand_PackageSourceMapping_NoSourcesMapped { + get { + return ResourceManager.GetString("PackageDownloadCommand_PackageSourceMapping_NoSourcesMapped", resourceCulture); + } + } + /// /// Looks up a localized string similar to The mapped source '{0}' for package '{1}' was not found among the configured sources.. /// diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx index 3a9b3a4c269..3d5ef962d17 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx @@ -1162,4 +1162,9 @@ Do not translate "PackageVersion" 0 - package source name 1 - package name + + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + {0} = package ID +{1} = comma-separated list of all configured sources + \ No newline at end of file diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf index f88a09768ef..30a22234ca0 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf @@ -929,6 +929,12 @@ Další informace najdete tady: https://docs.nuget.org/docs/reference/command-li Identifikátor balíčku (např. Newtonsoft.Json) + + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + {0} = package ID +{1} = comma-separated list of all configured sources + The mapped source '{0}' for package '{1}' was not found among the configured sources. Mapovaný zdroj {0} pro balíček {1} nebyl nalezen mezi nakonfigurovanými zdroji. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf index ef421b620f5..3e1714a7a6a 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf @@ -929,6 +929,12 @@ Weitere Informationen finden Sie unter: https://docs.nuget.org/docs/reference/co Paketbezeichner (z. B. „Newtonsoft.Json“). + + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + {0} = package ID +{1} = comma-separated list of all configured sources + The mapped source '{0}' for package '{1}' was not found among the configured sources. Die zugeordnete Quelle {0} für das Paket {1} wurde unter den konfigurierten Quellen nicht gefunden. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf index 00f17f02fe2..d2d3f4c00b0 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf @@ -929,6 +929,12 @@ Para obtener más información, visite https://docs.nuget.org/docs/reference/com Identificador del paquete (por ejemplo, 'Newtonsoft.Json'). + + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + {0} = package ID +{1} = comma-separated list of all configured sources + The mapped source '{0}' for package '{1}' was not found among the configured sources. No se encontró el origen asignado '{0}' para el paquete '{1}' entre los orígenes configurados. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf index 65fb46259d1..1ccb9edd8f7 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf @@ -929,6 +929,12 @@ Pour plus d'informations, visitez https://docs.nuget.org/docs/reference/command- Identificateur de package (par exemple, « Newtonsoft.Json »). + + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + {0} = package ID +{1} = comma-separated list of all configured sources + The mapped source '{0}' for package '{1}' was not found among the configured sources. La source mappée « {0} » pour le package « {1} » est introuvable parmi les sources configurées. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf index fadaec784f7..c784b60e6f5 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf @@ -929,6 +929,12 @@ Per altre informazioni, vedere https://docs.nuget.org/docs/reference/command-lin Identificatore del pacchetto ,ad esempio 'Newtonsoft.Json'. + + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + {0} = package ID +{1} = comma-separated list of all configured sources + The mapped source '{0}' for package '{1}' was not found among the configured sources. L'origine mappata '{0}' per il pacchetto '{1}' non è stata trovata tra le origini configurate. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf index b613c85a669..93ed0d07a40 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf @@ -929,6 +929,12 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r パッケージ識別子 (例: 'Newtonsoft.Json')。 + + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + {0} = package ID +{1} = comma-separated list of all configured sources + The mapped source '{0}' for package '{1}' was not found among the configured sources. パッケージ '{1}' のマップされたソース '{0}' が、構成済みのソースの中に見つかりませんでした。 diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf index 9b24629c526..8bce96c1054 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf @@ -929,6 +929,12 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 패키지 식별자(예: 'Newtonsoft.Json'). + + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + {0} = package ID +{1} = comma-separated list of all configured sources + The mapped source '{0}' for package '{1}' was not found among the configured sources. 구성된 원본 중 '{1}' 패키지에 대해 매핑된 원본 '{0}'을(를) 찾을 수 없습니다. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf index 6b9b4b48b69..540b8bb33e0 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf @@ -929,6 +929,12 @@ Aby uzyskać więcej informacji, odwiedź stronę https://docs.nuget.org/docs/re Identyfikator pakietu (np. „Newtonsoft.Json”). + + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + {0} = package ID +{1} = comma-separated list of all configured sources + The mapped source '{0}' for package '{1}' was not found among the configured sources. Zmapowane źródło „{0}” dla pakietu „{1}” nie zostało znalezione wśród skonfigurowanych źródeł. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf index 6bf07f8ac8a..19d526ab67b 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf @@ -929,6 +929,12 @@ Para obter mais informações, acesse https://docs.nuget.org/docs/reference/comm Identificador de pacote (por exemplo, "Newtonsoft.Json"). + + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + {0} = package ID +{1} = comma-separated list of all configured sources + The mapped source '{0}' for package '{1}' was not found among the configured sources. A origem mapeada "{0}" para o pacote "{1}" não foi encontrada entre as fontes configuradas. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf index 5e89925d95f..104a684865d 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf @@ -929,6 +929,12 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r Идентификатор пакета (например, "Newtonsoft.Json"). + + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + {0} = package ID +{1} = comma-separated list of all configured sources + The mapped source '{0}' for package '{1}' was not found among the configured sources. Сопоставленный источник "{0}" для пакета "{1}" не найден среди настроенных источников. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf index 11899374363..8345a2db6f0 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf @@ -930,6 +930,12 @@ Daha fazla bilgi için bkz. https://docs.nuget.org/docs/reference/command-line-r Paket tanımlayıcısı (örneğin ‘Newtonsoft.Json’). + + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + {0} = package ID +{1} = comma-separated list of all configured sources + The mapped source '{0}' for package '{1}' was not found among the configured sources. '{1}' paketi için eşlenen '{0}' kaynağı yapılandırılmış kaynaklar arasında bulunamadı. diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf index f3750eef157..e6bf8af7abd 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf @@ -929,6 +929,12 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 包标识符(例如 "Newtonsoft.Json")。 + + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + {0} = package ID +{1} = comma-separated list of all configured sources + The mapped source '{0}' for package '{1}' was not found among the configured sources. 在配置的源中找不到包 "{1}" 的映射源 "{0}"。 diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf index 56a5581cb3c..226ef4ed6cd 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf @@ -929,6 +929,12 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r 套件識別碼 (e.g. 'Newtonsoft.Json')。 + + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + Unable to download package '{0}'. PackageSourceMapping is enabled, but no mapped sources were found for this package. The following source(s) were not considered: {1}. + {0} = package ID +{1} = comma-separated list of all configured sources + The mapped source '{0}' for package '{1}' was not found among the configured sources. 在已設定的來源中找不到套件 '{1}' 的對應來源 '{0}'。 diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs index 4cd5238ec7e..a081d354ef2 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs @@ -620,6 +620,12 @@ public async Task RunAsync_WhenSourceMappingMapsToInsecureSource_AndAllowInsecur source }; + string expectedError = string.Format( + CultureInfo.CurrentCulture, + Strings.Error_HttpServerUsage, + "package download", + source); + // Act var exit = await PackageDownloadRunner.RunAsync( args, @@ -630,11 +636,6 @@ public async Task RunAsync_WhenSourceMappingMapsToInsecureSource_AndAllowInsecur // Assert exit.Should().Be(PackageDownloadRunner.ExitCodeError); - string expectedError = string.Format( - CultureInfo.CurrentCulture, - Strings.Error_HttpServerUsage, - "package download", - source); logger.Verify( l => l.LogError(expectedError), Times.AtLeastOnce); @@ -695,10 +696,8 @@ public async Task RunAsync_WhenSourceMappingMapsToSecureLocalSource_DoesNotLogEr File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{version}.nupkg")).Should().BeTrue(); } - [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task RunAsync_WhenSourceMappingEnabled_PackageMapsToNoSource(bool allowInsecureConnections) + [Fact] + public async Task RunAsync_WhenSourceMappingEnabled_PackageMapsToNoSource_Fails() { // Arrange using var context = new SimpleTestPathContext(); @@ -707,14 +706,14 @@ public async Task RunAsync_WhenSourceMappingEnabled_PackageMapsToNoSource(bool a context.Settings.AddPackageSourceMapping(source.Name, "Contoso.Not.*"); var settings = Settings.LoadSettingsGivenConfigPaths([context.Settings.ConfigPath]); - // package in source + // package var id = "Contoso.Package"; var version = "1.0.0"; string expectedError = string.Format( CultureInfo.CurrentCulture, - Strings.Error_HttpServerUsage, - "package download", + Strings.PackageDownloadCommand_PackageSourceMapping_NoSourcesMapped, + id, source); // arguments @@ -734,7 +733,6 @@ public async Task RunAsync_WhenSourceMappingEnabled_PackageMapsToNoSource(bool a } ], OutputDirectory = context.WorkingDirectory, - AllowInsecureConnections = allowInsecureConnections, }; // Act @@ -746,19 +744,10 @@ public async Task RunAsync_WhenSourceMappingEnabled_PackageMapsToNoSource(bool a CancellationToken.None); // Assert - if (allowInsecureConnections) - { - logger.Verify( - l => l.LogError(expectedError), - Times.Never); - } - else - { - exit.Should().Be(PackageDownloadRunner.ExitCodeError); - logger.Verify( - l => l.LogError(expectedError), - Times.Once); - } + exit.Should().Be(PackageDownloadRunner.ExitCodeError); + logger.Verify( + l => l.LogError(expectedError), + Times.Once); } [Fact] From 1c0792c2fb637c47ed50fb3e950f96ea7d2e5aa3 Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Thu, 20 Nov 2025 11:00:29 -0800 Subject: [PATCH 06/10] test case --- .../Download/PackageDownloadRunnerTests.cs | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs index a081d354ef2..d93d0f3523b 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs @@ -813,6 +813,67 @@ public async Task RunAsync_WhenSourceMappingMapsToInsecureSource_AndAllowInsecur File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{version}.nupkg")).Should().BeTrue(); } + [Theory] + [InlineData("A", "a")] + [InlineData("a", "A")] + [InlineData("SourceA", "sourcea")] + [InlineData("SOURCEA", "sourcea")] + public async Task RunAsync_WithVariousSourceMappingCasing_FindsSourceAndSucceeds(string sourceNameConfig, string sourceNameMapping) + { + // Arrange + using var context = new SimpleTestPathContext(); + string srcDir = context.PackageSource; + + var id = "Contoso.Casing"; + var version = "1.0.0"; + + await SimpleTestPackageUtility.CreateFullPackageAsync(srcDir, id, version); + + // Add source using one casing + context.Settings.AddSource(sourceNameConfig, srcDir); + + // Add mapping using different casing + context.Settings.AddPackageSourceMapping(sourceNameMapping, "Contoso.*"); + + var settings = Settings.LoadSettingsGivenConfigPaths([context.Settings.ConfigPath]); + + var args = new PackageDownloadArgs + { + Packages = + [ + new PackageWithNuGetVersion + { + Id = id, + NuGetVersion = NuGetVersion.Parse(version) + } + ], + OutputDirectory = context.WorkingDirectory, + AllowInsecureConnections = true + }; + + var logger = new Mock(MockBehavior.Loose); + + var packageSources = new List + { + new(srcDir, sourceNameConfig), + }; + + // Act + var exit = await PackageDownloadRunner.RunAsync( + args, + logger.Object, + packageSources, + settings, + CancellationToken.None); + + // Assert + exit.Should().Be(PackageDownloadRunner.ExitCodeSuccess); + + var installDir = Path.Combine(context.WorkingDirectory, id.ToLowerInvariant(), version); + Directory.Exists(installDir).Should().BeTrue(); + File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{version}.nupkg")).Should().BeTrue(); + } + [Fact] public async Task RunAsync_WhenMappedSourceMissing_LogsVerbose() { From aa0c466d95c90632462edbbf6fdf4c964a3a6a70 Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Thu, 20 Nov 2025 12:20:48 -0800 Subject: [PATCH 07/10] more tests --- .../Package/Download/PackageDownloadRunner.cs | 2 +- .../Download/PackageDownloadRunnerTests.cs | 12 +-- .../Download/PackageDownloadRunnerTests.cs | 99 +++++++++++++++++++ 3 files changed, 106 insertions(+), 7 deletions(-) diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs index f10ec9b8345..3714a1ed29a 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Download/PackageDownloadRunner.cs @@ -240,7 +240,7 @@ await ResolvePackageDownloadVersion( return (versionToDownload, downloadSourceRepository); } - private static IReadOnlyList GetMappedRepositories( + internal static IReadOnlyList GetMappedRepositories( IReadOnlyList mappedNames, IReadOnlyList allRepos, string packageId, diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs index d93d0f3523b..bb17a423089 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs @@ -638,7 +638,7 @@ public async Task RunAsync_WhenSourceMappingMapsToInsecureSource_AndAllowInsecur exit.Should().Be(PackageDownloadRunner.ExitCodeError); logger.Verify( l => l.LogError(expectedError), - Times.AtLeastOnce); + Times.Once); } @@ -701,7 +701,7 @@ public async Task RunAsync_WhenSourceMappingEnabled_PackageMapsToNoSource_Fails( { // Arrange using var context = new SimpleTestPathContext(); - PackageSource source = new("http://contoso.test/v3/index.json", "Insecure"); + PackageSource source = new(context.PackageSource, "source"); context.Settings.AddSource(source.Name, source.SourceUri.OriginalString); context.Settings.AddPackageSourceMapping(source.Name, "Contoso.Not.*"); var settings = Settings.LoadSettingsGivenConfigPaths([context.Settings.ConfigPath]); @@ -711,10 +711,10 @@ public async Task RunAsync_WhenSourceMappingEnabled_PackageMapsToNoSource_Fails( var version = "1.0.0"; string expectedError = string.Format( - CultureInfo.CurrentCulture, - Strings.PackageDownloadCommand_PackageSourceMapping_NoSourcesMapped, - id, - source); + CultureInfo.CurrentCulture, + Strings.PackageDownloadCommand_PackageSourceMapping_NoSourcesMapped, + id, + source); // arguments var logger = new Mock(MockBehavior.Loose); diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs index 7779eb52aeb..be841c33bec 100644 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs @@ -4,6 +4,7 @@ #nullable disable using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -202,4 +203,102 @@ public async Task ResolvePackageDownloadVersion_UnlistedPackage_BehavesAsExpecte foundRepo.Should().BeNull(); } } + + [Theory] + [InlineData("A", "a")] + [InlineData("a", "A")] + [InlineData("SourceA", "sourcea")] + [InlineData("SOURCEA", "sourcea")] + public void GetMappedRepositories_WithVariousSourceCasing_ReturnsOnlyApplicableSources(string sourceNameConfig, string sourceNameMapped) + { + // Arrange + var packageId = "Contoso.Package"; + var packageSource = new PackageSource(sourceNameConfig, sourceNameConfig); + var repository = new Mock(); + repository.Setup(r => r.PackageSource).Returns(packageSource); + var allRepos = new List { repository.Object }; + var mappedNames = new List { sourceNameMapped }; + var logger = new Mock(MockBehavior.Loose); + + // Act + var result = PackageDownloadRunner.GetMappedRepositories( + mappedNames, + allRepos, + packageId, + logger.Object); + + // Assert + result.Should().HaveCount(1); + result[0].PackageSource.Name.Should().Be(sourceNameConfig); + } + + [Fact] + public void GetMappedRepositories_WhenSourceMissing_LogsVerbose() + { + // Arrange + var packageId = "Contoso.Package"; + + var existingRepo = new Mock(); + existingRepo.Setup(r => r.PackageSource).Returns(new PackageSource("A", "A")); + + var allRepos = new List { existingRepo.Object }; + string mappedName = "MissingSource"; + var mappedNames = new List { mappedName }; + + string captured = ""; + var logger = new Mock(MockBehavior.Loose); + logger.Setup(l => l.LogVerbose(It.IsAny())) + .Callback(msg => captured += msg); + + string expectedLog = string.Format( + CultureInfo.CurrentCulture, + XPlat.Strings.PackageDownloadCommand_PackageSourceMapping_NoSuchSource, + mappedName, + packageId); + + // Act + var result = PackageDownloadRunner.GetMappedRepositories( + mappedNames, + allRepos, + packageId, + logger.Object); + + // Assert + result.Should().BeEmpty(); + captured.Should().Contain(expectedLog); + } + + [Fact] + public void GetMappedRepositories_WhenAllMappedSourcesExist_ReturnsAllMatchedRepositories() + { + // Arrange + var packageId = "Contoso.Package"; + + var packageSourceA = new PackageSource("Source1", "Source1"); + var packageSourceB = new PackageSource("Source2", "Source2"); + + var repoA = new Mock(); + repoA.Setup(r => r.PackageSource).Returns(packageSourceA); + + var repoB = new Mock(); + repoB.Setup(r => r.PackageSource).Returns(packageSourceB); + + var allRepos = new List { repoA.Object, repoB.Object }; + var mappedNames = new List { "Source1", "source2" }; + + var logger = new Mock(MockBehavior.Loose); + + // Act + var result = PackageDownloadRunner.GetMappedRepositories( + mappedNames, + allRepos, + packageId, + logger.Object); + + // Assert + Assert.Equal(2, result.Count); + Assert.Equal(packageSourceA.Name, result[0].PackageSource.Name); + Assert.Equal(packageSourceB.Name, result[1].PackageSource.Name); + logger.Verify(l => l.LogVerbose(It.IsAny()), Times.Never); + } } From a40d1f8af7a78200465866ce815438f5fee961fc Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Thu, 20 Nov 2025 12:30:44 -0800 Subject: [PATCH 08/10] remove --- .../Download/PackageDownloadRunnerTests.cs | 61 ------------------- 1 file changed, 61 deletions(-) diff --git a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs index bb17a423089..970967ce217 100644 --- a/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.XPlat.FuncTest/Package/Download/PackageDownloadRunnerTests.cs @@ -813,67 +813,6 @@ public async Task RunAsync_WhenSourceMappingMapsToInsecureSource_AndAllowInsecur File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{version}.nupkg")).Should().BeTrue(); } - [Theory] - [InlineData("A", "a")] - [InlineData("a", "A")] - [InlineData("SourceA", "sourcea")] - [InlineData("SOURCEA", "sourcea")] - public async Task RunAsync_WithVariousSourceMappingCasing_FindsSourceAndSucceeds(string sourceNameConfig, string sourceNameMapping) - { - // Arrange - using var context = new SimpleTestPathContext(); - string srcDir = context.PackageSource; - - var id = "Contoso.Casing"; - var version = "1.0.0"; - - await SimpleTestPackageUtility.CreateFullPackageAsync(srcDir, id, version); - - // Add source using one casing - context.Settings.AddSource(sourceNameConfig, srcDir); - - // Add mapping using different casing - context.Settings.AddPackageSourceMapping(sourceNameMapping, "Contoso.*"); - - var settings = Settings.LoadSettingsGivenConfigPaths([context.Settings.ConfigPath]); - - var args = new PackageDownloadArgs - { - Packages = - [ - new PackageWithNuGetVersion - { - Id = id, - NuGetVersion = NuGetVersion.Parse(version) - } - ], - OutputDirectory = context.WorkingDirectory, - AllowInsecureConnections = true - }; - - var logger = new Mock(MockBehavior.Loose); - - var packageSources = new List - { - new(srcDir, sourceNameConfig), - }; - - // Act - var exit = await PackageDownloadRunner.RunAsync( - args, - logger.Object, - packageSources, - settings, - CancellationToken.None); - - // Assert - exit.Should().Be(PackageDownloadRunner.ExitCodeSuccess); - - var installDir = Path.Combine(context.WorkingDirectory, id.ToLowerInvariant(), version); - Directory.Exists(installDir).Should().BeTrue(); - File.Exists(Path.Combine(installDir, $"{id.ToLowerInvariant()}.{version}.nupkg")).Should().BeTrue(); - } - [Fact] public async Task RunAsync_WhenMappedSourceMissing_LogsVerbose() { From 62c2dfa6ed837bbd4cb9bd8d4387f7da49865b0f Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Thu, 20 Nov 2025 17:54:46 -0800 Subject: [PATCH 09/10] use real --- .../Download/PackageDownloadRunnerTests.cs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs index be841c33bec..e150b03e25d 100644 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Download/PackageDownloadRunnerTests.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -214,9 +215,8 @@ public void GetMappedRepositories_WithVariousSourceCasing_ReturnsOnlyApplicableS // Arrange var packageId = "Contoso.Package"; var packageSource = new PackageSource(sourceNameConfig, sourceNameConfig); - var repository = new Mock(); - repository.Setup(r => r.PackageSource).Returns(packageSource); - var allRepos = new List { repository.Object }; + var repository = new SourceRepository(packageSource, Array.Empty()); + var allRepos = new List { repository }; var mappedNames = new List { sourceNameMapped }; var logger = new Mock(MockBehavior.Loose); @@ -238,10 +238,9 @@ public void GetMappedRepositories_WhenSourceMissing_LogsVerbose() // Arrange var packageId = "Contoso.Package"; - var existingRepo = new Mock(); - existingRepo.Setup(r => r.PackageSource).Returns(new PackageSource("A", "A")); + var existingRepo = new SourceRepository(new PackageSource("A", "A"), Array.Empty()); - var allRepos = new List { existingRepo.Object }; + var allRepos = new List { existingRepo }; string mappedName = "MissingSource"; var mappedNames = new List { mappedName }; @@ -277,13 +276,10 @@ public void GetMappedRepositories_WhenAllMappedSourcesExist_ReturnsAllMatchedRep var packageSourceA = new PackageSource("Source1", "Source1"); var packageSourceB = new PackageSource("Source2", "Source2"); - var repoA = new Mock(); - repoA.Setup(r => r.PackageSource).Returns(packageSourceA); + var repoA = new SourceRepository(packageSourceA, Array.Empty()); + var repoB = new SourceRepository(packageSourceB, Array.Empty()); - var repoB = new Mock(); - repoB.Setup(r => r.PackageSource).Returns(packageSourceB); - - var allRepos = new List { repoA.Object, repoB.Object }; + var allRepos = new List { repoA, repoB }; var mappedNames = new List { "Source1", "source2" }; var logger = new Mock(MockBehavior.Loose); From 9ee267843acc70ab5b9e8c96191a8350b85525c2 Mon Sep 17 00:00:00 2001 From: Nigusu Yenework Date: Mon, 24 Nov 2025 14:35:35 -0800 Subject: [PATCH 10/10] test remotewalker --- .../RemoteWalkContextTests.cs | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/test/NuGet.Core.Tests/NuGet.DependencyResolver.Core.Tests/RemoteWalkContextTests.cs b/test/NuGet.Core.Tests/NuGet.DependencyResolver.Core.Tests/RemoteWalkContextTests.cs index 26b1e97bde4..73b591c5271 100644 --- a/test/NuGet.Core.Tests/NuGet.DependencyResolver.Core.Tests/RemoteWalkContextTests.cs +++ b/test/NuGet.Core.Tests/NuGet.DependencyResolver.Core.Tests/RemoteWalkContextTests.cs @@ -109,6 +109,80 @@ public void FilterDependencyProvidersForLibrary_WhenPackagePatternToSourceMappin Assert.Empty(providers); } + [Fact] + public void FilterDependencyProvidersForLibrary_WhenPackagePatternMappedToMultipleSources_ReturnsAllApplicableProviders() + { + // Arrange + var logger = new TestLogger(); + + // package source mapping configuration: same pattern "x" mapped to multiple sources + Dictionary> patterns = new() + { + { "Source1", new List { "x" } }, + { "Source2", new List { "x" } }, + { "Source3", new List { "y" } } + }; + PackageSourceMapping sourceMappingConfiguration = new(patterns); + + var context = new TestRemoteWalkContext(sourceMappingConfiguration, logger); + + // Source1 + var remoteProvider1 = CreateRemoteDependencyProvider("Source1"); + context.RemoteLibraryProviders.Add(remoteProvider1.Object); + + // Source2 + var remoteProvider2 = CreateRemoteDependencyProvider("Source2"); + context.RemoteLibraryProviders.Add(remoteProvider2.Object); + + // Source3 + var remoteProvider3 = CreateRemoteDependencyProvider("Source3"); + context.RemoteLibraryProviders.Add(remoteProvider3.Object); + + var libraryRange = new LibraryRange("x", Versioning.VersionRange.None, LibraryDependencyTarget.Package); + + // Act + IList providers = context.FilterDependencyProvidersForLibrary(libraryRange); + + // Assert + Assert.Equal(2, providers.Count); + Assert.Equal("Source1", providers[0].Source.Name); + Assert.Equal("Source2", providers[1].Source.Name); + } + + [Fact] + public void FilterDependencyProvidersForLibrary_WhenMappingIncludesMissingSource_IgnoresMissingSource() + { + // Arrange + var logger = new TestLogger(); + + // "x" is mapped to an existing source and a non-existent one + Dictionary> patterns = new() + { + { "Source1", new List { "x" } }, + { "MissingSource", new List { "x" } } + }; + PackageSourceMapping sourceMappingConfiguration = new(patterns); + + var context = new TestRemoteWalkContext(sourceMappingConfiguration, logger); + + // Only Source1 exists as a provider + var remoteProvider1 = CreateRemoteDependencyProvider("Source1"); + context.RemoteLibraryProviders.Add(remoteProvider1.Object); + + // Another unrelated provider + var remoteProvider2 = CreateRemoteDependencyProvider("OtherSource"); + context.RemoteLibraryProviders.Add(remoteProvider2.Object); + + var libraryRange = new LibraryRange("x", Versioning.VersionRange.None, LibraryDependencyTarget.Package); + + // Act + IList providers = context.FilterDependencyProvidersForLibrary(libraryRange); + + // Assert + Assert.Single(providers); + Assert.Equal("Source1", providers[0].Source.Name); + } + private Mock CreateRemoteDependencyProvider(string source) { var remoteProvider = new Mock();