diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryEnvironment.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryEnvironment.cs
new file mode 100644
index 00000000000..dc55b8c4bc9
--- /dev/null
+++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryEnvironment.cs
@@ -0,0 +1,23 @@
+namespace NuGetUpdater.Core.Test;
+
+public class TemporaryEnvironment : IDisposable
+{
+ private readonly List<(string Name, string? Value)> _originalVariables = new();
+
+ public TemporaryEnvironment((string Name, string Value)[] variables)
+ {
+ foreach (var (name, value) in variables)
+ {
+ _originalVariables.Add((name, Environment.GetEnvironmentVariable(name)));
+ Environment.SetEnvironmentVariable(name, value);
+ }
+ }
+
+ public void Dispose()
+ {
+ foreach (var (name, value) in _originalVariables)
+ {
+ Environment.SetEnvironmentVariable(name, value);
+ }
+ }
+}
diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs
index 6217f51f6a6..993c79f9b5d 100644
--- a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs
+++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs
@@ -1,6 +1,9 @@
using System.Collections.Immutable;
+using System.Text;
using System.Text.Json;
+using NuGet;
+
using NuGetUpdater.Core.Updater;
using Xunit;
@@ -1663,67 +1666,173 @@ await TestUpdateForProject("Some.Package", "7.0.1", "13.0.1",
[Fact]
public async Task PackageCanBeUpdatedWhenAnotherInstalledPackageHasBeenDelisted()
{
- // updating one package (Newtonsoft.Json) when another installed package (FSharp.Core/5.0.3-beta.21369.4) has been delisted
- await TestUpdateForProject("Newtonsoft.Json", "7.0.1", "13.0.1",
+ // updating one package (Some.Package) when another installed package (Delisted.Package/5.0.0) has been delisted
+ // this test can't be faked with a local package source and requires an HTTP endpoint; the important part is
+ // the `"listed": false` in the registration index
+ static (int, byte[]) TestHttpHandler(string uriString)
+ {
+ var uri = new Uri(uriString, UriKind.Absolute);
+ var baseUrl = $"{uri.Scheme}://{uri.Host}:{uri.Port}";
+ return uri.PathAndQuery switch
+ {
+ "/index.json" => (200, Encoding.UTF8.GetBytes($$"""
+ {
+ "version": "3.0.0",
+ "resources": [
+ {
+ "@id": "{{baseUrl}}/download",
+ "@type": "PackageBaseAddress/3.0.0"
+ },
+ {
+ "@id": "{{baseUrl}}/query",
+ "@type": "SearchQueryService"
+ },
+ {
+ "@id": "{{baseUrl}}/registrations",
+ "@type": "RegistrationsBaseUrl"
+ }
+ ]
+ }
+ """)),
+ "/registrations/delisted.package/index.json" => (200, Encoding.UTF8.GetBytes($$"""
+ {
+ "count": 1,
+ "items": [
+ {
+ "lower": "5.0.0",
+ "upper": "5.0.0",
+ "items": [
+ {
+ "catalogEntry": {
+ "id": "Delisted.Package",
+ "listed": false,
+ "version": "5.0.0"
+ },
+ "packageContent": "{{baseUrl}}/download/delisted.package/5.0.0/delisted.package.5.0.0.nupkg",
+ }
+ ]
+ }
+ ]
+ }
+ """)),
+ "/registrations/some.package/index.json" => (200, Encoding.UTF8.GetBytes($$"""
+ {
+ "count": 1,
+ "items": [
+ {
+ "lower": "1.0.0",
+ "upper": "2.0.0",
+ "items": [
+ {
+ "catalogEntry": {
+ "id": "Some.Package",
+ "listed": true,
+ "version": "1.0.0"
+ },
+ "packageContent": "{{baseUrl}}/download/some.package/1.0.0/some.package.1.0.0.nupkg",
+ },
+ {
+ "catalogEntry": {
+ "id": "Some.Package",
+ "listed": true,
+ "version": "2.0.0"
+ },
+ "packageContent": "{{baseUrl}}/download/some.package/2.0.0/some.package.2.0.0.nupkg",
+ }
+ ]
+ }
+ ]
+ }
+ """)),
+ "/download/delisted.package/5.0.0/delisted.package.5.0.0.nupkg" =>
+ (200, MockNuGetPackage.CreateSimplePackage("Delisted.Package", "5.0.0", "net45").GetZipStream().ReadAllBytes()),
+ "/download/some.package/1.0.0/some.package.1.0.0.nupkg" =>
+ (200, MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net45").GetZipStream().ReadAllBytes()),
+ "/download/some.package/2.0.0/some.package.2.0.0.nupkg" =>
+ (200, MockNuGetPackage.CreateSimplePackage("Some.Package", "2.0.0", "net45").GetZipStream().ReadAllBytes()),
+ _ => (404, Encoding.UTF8.GetBytes("{}")), // everything is missing
+ };
+ }
+ using var cache = new TemporaryDirectory();
+ using var env = new TemporaryEnvironment([
+ ("NUGET_PACKAGES", Path.Join(cache.DirectoryPath, "NUGET_PACKAGES")),
+ ("NUGET_HTTP_CACHE_PATH", Path.Join(cache.DirectoryPath, "NUGET_HTTP_CACHE_PATH")),
+ ("NUGET_SCRATCH", Path.Join(cache.DirectoryPath, "NUGET_SCRATCH")),
+ ("NUGET_PLUGINS_CACHE_PATH", Path.Join(cache.DirectoryPath, "NUGET_PLUGINS_CACHE_PATH")),
+ ]);
+ using var http = TestHttpServer.CreateTestServer(TestHttpHandler);
+ await TestUpdateForProject("Some.Package", "1.0.0", "2.0.0",
// existing
projectContents: """
-
-
-
- v4.6.2
-
-
-
-
-
-
- packages\FSharp.Core.5.0.3-beta.21369.4\lib\netstandard2.0\FSharp.Core.dll
- True
-
-
- packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll
- True
-
-
-
-
- """,
+
+
+
+ v4.6.2
+
+
+
+
+
+
+ packages\Delisted.Package.5.0.0\lib\net45\Delisted.Package.dll
+ True
+
+
+ packages\Some.Package.1.0.0\lib\net45\Some.Package.dll
+ True
+
+
+
+
+ """,
packagesConfigContents: """
-
-
-
-
- """,
+
+
+
+
+ """,
+ additionalFiles:
+ [
+ ("NuGet.Config", $"""
+
+
+
+
+
+
+ """)
+ ],
// expected
expectedProjectContents: """
-
-
-
- v4.6.2
-
-
-
-
-
-
- packages\FSharp.Core.5.0.3-beta.21369.4\lib\netstandard2.0\FSharp.Core.dll
- True
-
-
- packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll
- True
-
-
-
-
- """,
+
+
+
+ v4.6.2
+
+
+
+
+
+
+ packages\Delisted.Package.5.0.0\lib\net45\Delisted.Package.dll
+ True
+
+
+ packages\Some.Package.2.0.0\lib\net45\Some.Package.dll
+ True
+
+
+
+
+ """,
expectedPackagesConfigContents: """
-
-
-
-
-
- """);
+
+
+
+
+
+ """
+ );
}
[Fact]