dependabot-nuget 0.311.0 → 0.313.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Run.cs +2 -2
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +7 -0
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/BadResponseException.cs +12 -0
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +121 -9
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +64 -17
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UnknownError.cs +12 -2
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/HttpApiHandler.cs +19 -50
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/IApiHandler.cs +57 -7
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +10 -5
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +13 -1
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +15 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/VersionFinderTests.cs +74 -1
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Clone/CloneWorkerTests.cs +1 -1
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +3 -9
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +1 -2
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +58 -2
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/HttpApiHandlerTests.cs +157 -0
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/JobErrorBaseTests.cs +86 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MessageReportTests.cs +1 -2
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +227 -6
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +40 -11
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/TestApiHandler.cs +2 -39
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestHttpServer.cs +9 -5
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackageReferenceUpdaterTests.cs +99 -1
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +16 -0
  27. metadata +26 -23
@@ -16,8 +16,6 @@ using Xunit;
16
16
 
17
17
  namespace NuGetUpdater.Core.Test.Run;
18
18
 
19
- using static NuGetUpdater.Core.Utilities.EOLHandling;
20
-
21
19
  using TestFile = (string Path, string Content);
22
20
  using RawTestFile = (string Path, byte[] Content);
23
21
 
@@ -2777,6 +2775,162 @@ public class RunWorkerTests
2777
2775
  );
2778
2776
  }
2779
2777
 
2778
+ [Fact]
2779
+ public async Task AnalysisResultWithoutUpdatedDependenciesDoesNotCauseError()
2780
+ {
2781
+ await RunAsync(
2782
+ job: new()
2783
+ {
2784
+ Source = new()
2785
+ {
2786
+ Provider = "github",
2787
+ Repo = "test/repo",
2788
+ },
2789
+ Dependencies = [
2790
+ "Some.Package"
2791
+ ],
2792
+ },
2793
+ files: [
2794
+ ("project.csproj", "contents irrelevant")
2795
+ ],
2796
+ discoveryWorker: new TestDiscoveryWorker(_input =>
2797
+ {
2798
+ return Task.FromResult(new WorkspaceDiscoveryResult()
2799
+ {
2800
+ Path = "",
2801
+ Projects = [
2802
+ new()
2803
+ {
2804
+ FilePath = "project.csproj",
2805
+ Dependencies = [
2806
+ new("Some.Package", "1.0.0", DependencyType.PackageReference)
2807
+ ],
2808
+ ImportedFiles = [],
2809
+ AdditionalFiles = [],
2810
+ }
2811
+ ]
2812
+ });
2813
+ }),
2814
+ analyzeWorker: new TestAnalyzeWorker(_input =>
2815
+ {
2816
+ return Task.FromResult(new AnalysisResult()
2817
+ {
2818
+ CanUpdate = true,
2819
+ UpdatedVersion = "1.1.0",
2820
+ UpdatedDependencies = [], // this is what was causing the problem
2821
+ });
2822
+ }),
2823
+ updaterWorker: new TestUpdaterWorker(async input =>
2824
+ {
2825
+ var repoRootPath = input.Item1;
2826
+ var filePath = input.Item2;
2827
+ await File.WriteAllTextAsync(Path.Join(repoRootPath, filePath), "updated contents irrelevant");
2828
+ return new UpdateOperationResult()
2829
+ {
2830
+ UpdateOperations = [
2831
+ new DirectUpdate()
2832
+ {
2833
+ DependencyName = "Some.Package",
2834
+ NewVersion = NuGetVersion.Parse("1.1.0"),
2835
+ UpdatedFiles = ["project.csproj"]
2836
+ }
2837
+ ]
2838
+ };
2839
+ }),
2840
+ expectedResult: new()
2841
+ {
2842
+ Base64DependencyFiles = [
2843
+ new()
2844
+ {
2845
+ Directory = "/",
2846
+ Name = "project.csproj",
2847
+ Content = Convert.ToBase64String(Encoding.UTF8.GetBytes("contents irrelevant")),
2848
+ ContentEncoding = "base64"
2849
+ }
2850
+ ],
2851
+ BaseCommitSha = "TEST-COMMIT-SHA",
2852
+ },
2853
+ expectedApiMessages: [
2854
+ new UpdatedDependencyList()
2855
+ {
2856
+ Dependencies = [
2857
+ new()
2858
+ {
2859
+ Name = "Some.Package",
2860
+ Version = "1.0.0",
2861
+ Requirements = [
2862
+ new()
2863
+ {
2864
+ Requirement = "1.0.0",
2865
+ File = "/project.csproj",
2866
+ Groups = ["dependencies"],
2867
+ }
2868
+ ]
2869
+ }
2870
+ ],
2871
+ DependencyFiles = ["/project.csproj"]
2872
+ },
2873
+ new IncrementMetric()
2874
+ {
2875
+ Metric = "updater.started",
2876
+ Tags = new()
2877
+ {
2878
+ ["operation"] = "group_update_all_versions"
2879
+ }
2880
+ },
2881
+ new CreatePullRequest()
2882
+ {
2883
+ Dependencies =
2884
+ [
2885
+ new ReportedDependency()
2886
+ {
2887
+ Name = "Some.Package",
2888
+ Version = "1.1.0",
2889
+ Requirements =
2890
+ [
2891
+ new ReportedRequirement()
2892
+ {
2893
+ Requirement = "1.1.0",
2894
+ File = "/project.csproj",
2895
+ Groups = ["dependencies"],
2896
+ Source = new()
2897
+ {
2898
+ SourceUrl = null,
2899
+ Type = "nuget_repo",
2900
+ }
2901
+ }
2902
+ ],
2903
+ PreviousVersion = "1.0.0",
2904
+ PreviousRequirements =
2905
+ [
2906
+ new ReportedRequirement()
2907
+ {
2908
+ Requirement = "1.0.0",
2909
+ File = "/project.csproj",
2910
+ Groups = ["dependencies"],
2911
+ }
2912
+ ],
2913
+ }
2914
+ ],
2915
+ UpdatedDependencyFiles =
2916
+ [
2917
+ new DependencyFile()
2918
+ {
2919
+ Name = "project.csproj",
2920
+ Directory = "/",
2921
+ Content = "updated contents irrelevant",
2922
+ },
2923
+ ],
2924
+ BaseCommitSha = "TEST-COMMIT-SHA",
2925
+ CommitMessage = TestPullRequestCommitMessage,
2926
+ PrTitle = TestPullRequestTitle,
2927
+ PrBody = TestPullRequestBody,
2928
+ },
2929
+ new MarkAsProcessed("TEST-COMMIT-SHA"),
2930
+ ]
2931
+ );
2932
+ }
2933
+
2780
2934
  [Fact]
2781
2935
  public async Task ByteOrderMarksAreDetectedAndRestored()
2782
2936
  {
@@ -3266,6 +3420,50 @@ public class RunWorkerTests
3266
3420
  );
3267
3421
  }
3268
3422
 
3423
+ [Fact]
3424
+ public async Task UnknownErrorsGenerateAllRequiredApiCalls()
3425
+ {
3426
+ await RunAsync(
3427
+ job: new Job()
3428
+ {
3429
+ Source = new()
3430
+ {
3431
+ Provider = "github",
3432
+ Repo = "test/repo",
3433
+ Directory = "some-dir",
3434
+ }
3435
+ },
3436
+ packages: [],
3437
+ files: [],
3438
+ discoveryWorker: new TestDiscoveryWorker(_input =>
3439
+ {
3440
+ throw new FileNotFoundException("some required file is missing");
3441
+ }),
3442
+ analyzeWorker: TestAnalyzeWorker.FromResults(), // unreachable
3443
+ updaterWorker: TestUpdaterWorker.FromResults(), // unreachable
3444
+ expectedResult: new RunResult()
3445
+ {
3446
+ Base64DependencyFiles = [],
3447
+ BaseCommitSha = "TEST-COMMIT-SHA",
3448
+ },
3449
+ expectedApiMessages:
3450
+ [
3451
+ new UnknownError(new FileNotFoundException("some required file is missing"), "TEST-JOB-ID"), // from record_update_job_error
3452
+ new UnknownError(new FileNotFoundException("some required file is missing"), "TEST-JOB-ID"), // from record_update_job_unknown_error
3453
+ new IncrementMetric()
3454
+ {
3455
+ Metric = "updater.update_job_unknown_error",
3456
+ Tags = new()
3457
+ {
3458
+ ["package_manager"] = "nuget",
3459
+ ["class_name"] = "FileNotFoundException"
3460
+ }
3461
+ },
3462
+ new MarkAsProcessed("TEST-COMMIT-SHA")
3463
+ ]
3464
+ );
3465
+ }
3466
+
3269
3467
  internal static Task RunAsync(Job job, TestFile[] files, IDiscoveryWorker? discoveryWorker, IAnalyzeWorker? analyzeWorker, IUpdaterWorker? updaterWorker, RunResult expectedResult, object[] expectedApiMessages, MockNuGetPackage[]? packages = null, ExperimentsManager? experimentsManager = null, string? repoContentsPath = null)
3270
3468
  {
3271
3469
  var rawTestFiles = files.Select(f => (f.Path, Encoding.UTF8.GetBytes(f.Content))).ToArray();
@@ -3300,13 +3498,36 @@ public class RunWorkerTests
3300
3498
  var actualResult = await worker.RunAsync(job, repoContentsPathDirectoryInfo, "TEST-COMMIT-SHA");
3301
3499
  var actualApiMessages = testApiHandler.ReceivedMessages
3302
3500
  .Select(m =>
3303
- m.Object switch
3501
+ {
3502
+ object newObject;
3503
+ switch (m.Object)
3304
3504
  {
3305
3505
  // this isn't the place to verify the generated text
3306
- CreatePullRequest create => (m.Type, create with { CommitMessage = TestPullRequestCommitMessage, PrTitle = TestPullRequestTitle, PrBody = TestPullRequestBody }),
3307
- UpdatePullRequest update => (m.Type, update with { CommitMessage = TestPullRequestCommitMessage, PrTitle = TestPullRequestTitle, PrBody = TestPullRequestBody }),
3308
- _ => m,
3506
+ case CreatePullRequest create:
3507
+ newObject = create with { CommitMessage = TestPullRequestCommitMessage, PrTitle = TestPullRequestTitle, PrBody = TestPullRequestBody };
3508
+ break;
3509
+ case UpdatePullRequest update:
3510
+ newObject = update with { CommitMessage = TestPullRequestCommitMessage, PrTitle = TestPullRequestTitle, PrBody = TestPullRequestBody };
3511
+ break;
3512
+ // don't test callstacks
3513
+ case UnknownError unknown:
3514
+ var message = (string)unknown.Details["error-message"];
3515
+ var stackTraceOffset = message.IndexOf('\n');
3516
+ if (stackTraceOffset >= 0)
3517
+ {
3518
+ var messageWithoutStackTrace = message[..stackTraceOffset].TrimEnd('\r');
3519
+ unknown.Details["error-message"] = messageWithoutStackTrace;
3520
+ }
3521
+
3522
+ newObject = unknown;
3523
+ break;
3524
+ default:
3525
+ newObject = m.Object;
3526
+ break;
3309
3527
  }
3528
+
3529
+ return (m.Type, Object: newObject);
3530
+ }
3310
3531
  ).ToArray();
3311
3532
 
3312
3533
  // assert
@@ -8,7 +8,7 @@ using Xunit;
8
8
 
9
9
  namespace NuGetUpdater.Core.Test.Run;
10
10
 
11
- public class SerializationTests
11
+ public class SerializationTests : TestBase
12
12
  {
13
13
  [Fact]
14
14
  public void DeserializeJob()
@@ -260,15 +260,9 @@ public class SerializationTests
260
260
  }
261
261
 
262
262
  [Theory]
263
- [MemberData(nameof(DeserializeErrorTypesData))]
263
+ [MemberData(nameof(SerializeErrorTypesData))]
264
264
  public void SerializeError(JobErrorBase error, string expectedSerialization)
265
265
  {
266
- if (error is UnknownError unknown)
267
- {
268
- // special case the exception's call stack to make it testable
269
- unknown.Details["error-backtrace"] = "TEST-BACKTRACE";
270
- }
271
-
272
266
  var actual = HttpApiHandler.Serialize(error);
273
267
  Assert.Equal(expectedSerialization, actual);
274
268
  }
@@ -279,7 +273,7 @@ public class SerializationTests
279
273
  var untestedTypes = typeof(JobErrorBase).Assembly.GetTypes()
280
274
  .Where(t => t.IsSubclassOf(typeof(JobErrorBase)))
281
275
  .ToHashSet();
282
- foreach (object?[] data in DeserializeErrorTypesData())
276
+ foreach (object?[] data in SerializeErrorTypesData())
283
277
  {
284
278
  var testedErrorType = data[0]!.GetType();
285
279
  untestedTypes.Remove(testedErrorType);
@@ -601,7 +595,42 @@ public class SerializationTests
601
595
  Assert.Equal(expected, actual);
602
596
  }
603
597
 
604
- public static IEnumerable<object?[]> DeserializeErrorTypesData()
598
+ [Fact]
599
+ public void SerializeRealUnknownErrorWithInnerException()
600
+ {
601
+ // arrange
602
+ using var tempDir = new TemporaryDirectory();
603
+ var action = new Action(() =>
604
+ {
605
+ try
606
+ {
607
+ throw new NotImplementedException("inner message");
608
+ }
609
+ catch (Exception ex)
610
+ {
611
+ throw new InvalidOperationException("outer message", ex);
612
+ }
613
+ });
614
+ var ex = Assert.Throws<InvalidOperationException>(action);
615
+
616
+ // act
617
+ var error = JobErrorBase.ErrorFromException(ex, "TEST-JOB-ID", tempDir.DirectoryPath);
618
+
619
+ // assert
620
+ // real exception message should look like this:
621
+ // System.InvalidOperationException: outer message
622
+ // ---> System.NotImplementedException: inner message
623
+ // at Namespace.Class.Method() in file.cs:line 123
624
+ // --- End of inner exception stack trace ---
625
+ // at Namespace.Class.Method() in file.cs:line 456
626
+ var errorMessage = Assert.IsType<string>(error.Details["error-message"]);
627
+ var lines = errorMessage.Split('\n').Select(l => l.TrimEnd('\r')).ToArray();
628
+ Assert.Equal("System.InvalidOperationException: outer message", lines[0]);
629
+ Assert.Equal(" ---> System.NotImplementedException: inner message", lines[1]);
630
+ Assert.Contains(" --- End of inner exception stack trace ---", lines[2..]);
631
+ }
632
+
633
+ public static IEnumerable<object?[]> SerializeErrorTypesData()
605
634
  {
606
635
  yield return
607
636
  [
@@ -679,7 +708,7 @@ public class SerializationTests
679
708
  [
680
709
  new UnknownError(new Exception("some message"), "JOB-ID"),
681
710
  """
682
- {"data":{"error-type":"unknown_error","error-details":{"error-class":"Exception","error-message":"some message","error-backtrace":"TEST-BACKTRACE","package-manager":"nuget","job-id":"JOB-ID"}}}
711
+ {"data":{"error-type":"unknown_error","error-details":{"error-class":"Exception","error-message":"System.Exception: some message","error-backtrace":"","package-manager":"nuget","job-id":"JOB-ID"}}}
683
712
  """
684
713
  ];
685
714
 
@@ -1,5 +1,4 @@
1
1
  using NuGetUpdater.Core.Run;
2
- using NuGetUpdater.Core.Run.ApiModel;
3
2
 
4
3
  namespace NuGetUpdater.Core.Test;
5
4
 
@@ -9,45 +8,9 @@ internal class TestApiHandler : IApiHandler
9
8
 
10
9
  public IEnumerable<(Type Type, object Object)> ReceivedMessages => _receivedMessages;
11
10
 
12
- public Task RecordUpdateJobError(JobErrorBase error)
11
+ public Task SendAsync(string endpoint, object body, string method)
13
12
  {
14
- _receivedMessages.Add((error.GetType(), error));
15
- return Task.CompletedTask;
16
- }
17
-
18
- public Task UpdateDependencyList(UpdatedDependencyList updatedDependencyList)
19
- {
20
- _receivedMessages.Add((updatedDependencyList.GetType(), updatedDependencyList));
21
- return Task.CompletedTask;
22
- }
23
-
24
- public Task IncrementMetric(IncrementMetric incrementMetric)
25
- {
26
- _receivedMessages.Add((incrementMetric.GetType(), incrementMetric));
27
- return Task.CompletedTask;
28
- }
29
-
30
- public Task CreatePullRequest(CreatePullRequest createPullRequest)
31
- {
32
- _receivedMessages.Add((createPullRequest.GetType(), createPullRequest));
33
- return Task.CompletedTask;
34
- }
35
-
36
- public Task ClosePullRequest(ClosePullRequest closePullRequest)
37
- {
38
- _receivedMessages.Add((closePullRequest.GetType(), closePullRequest));
39
- return Task.CompletedTask;
40
- }
41
-
42
- public Task UpdatePullRequest(UpdatePullRequest updatePullRequest)
43
- {
44
- _receivedMessages.Add((updatePullRequest.GetType(), updatePullRequest));
45
- return Task.CompletedTask;
46
- }
47
-
48
- public Task MarkAsProcessed(MarkAsProcessed markAsProcessed)
49
- {
50
- _receivedMessages.Add((markAsProcessed.GetType(), markAsProcessed));
13
+ _receivedMessages.Add((body.GetType(), body));
51
14
  return Task.CompletedTask;
52
15
  }
53
16
  }
@@ -10,13 +10,13 @@ namespace NuGetUpdater.Core.Test
10
10
  {
11
11
  public class TestHttpServer : IDisposable
12
12
  {
13
- private readonly Func<string, string, (int, byte[])> _requestHandler;
13
+ private readonly Func<string, string, (int, byte[]?)> _requestHandler;
14
14
  private readonly HttpListener _listener;
15
15
  private bool _runServer = true;
16
16
 
17
17
  public string BaseUrl { get; }
18
18
 
19
- private TestHttpServer(string baseurl, Func<string, string, (int, byte[])> requestHandler)
19
+ private TestHttpServer(string baseurl, Func<string, string, (int, byte[]?)> requestHandler)
20
20
  {
21
21
  BaseUrl = baseurl;
22
22
  _requestHandler = requestHandler;
@@ -45,7 +45,11 @@ namespace NuGetUpdater.Core.Test
45
45
  var context = await _listener.GetContextAsync();
46
46
  var (statusCode, response) = _requestHandler(context.Request.HttpMethod, context.Request.Url!.AbsoluteUri);
47
47
  context.Response.StatusCode = statusCode;
48
- await context.Response.OutputStream.WriteAsync(response);
48
+ if (response is not null)
49
+ {
50
+ await context.Response.OutputStream.WriteAsync(response);
51
+ }
52
+
49
53
  context.Response.Close();
50
54
  }
51
55
  }
@@ -57,7 +61,7 @@ namespace NuGetUpdater.Core.Test
57
61
  return CreateTestServer((method, url) => requestHandler(url));
58
62
  }
59
63
 
60
- public static TestHttpServer CreateTestServer(Func<string, string, (int, byte[])> requestHandler)
64
+ public static TestHttpServer CreateTestServer(Func<string, string, (int, byte[]?)> requestHandler)
61
65
  {
62
66
  // static lock to ensure the port is not recycled after `FindFreePort()` and before we can start the real server
63
67
  lock (PortGate)
@@ -77,7 +81,7 @@ namespace NuGetUpdater.Core.Test
77
81
 
78
82
  public static TestHttpServer CreateTestStringServer(Func<string, string, (int, string)> requestHandler)
79
83
  {
80
- Func<string, string, (int, byte[])> bytesRequestHandler = (method, url) =>
84
+ Func<string, string, (int, byte[]?)> bytesRequestHandler = (method, url) =>
81
85
  {
82
86
  var (statusCode, response) = requestHandler(method, url);
83
87
  return (statusCode, Encoding.UTF8.GetBytes(response));
@@ -55,7 +55,7 @@ public class PackageReferenceUpdaterTests
55
55
 
56
56
  // assert
57
57
  await buildFile.SaveAsync();
58
- var actualContents = await File.ReadAllTextAsync(fullProjectPath);
58
+ var actualContents = await File.ReadAllTextAsync(fullProjectPath, TestContext.Current.CancellationToken);
59
59
  var expectedContents = """
60
60
  <Project Sdk="Microsoft.NET.Sdk">
61
61
  <PropertyGroup>
@@ -71,6 +71,104 @@ public class PackageReferenceUpdaterTests
71
71
  Assert.Equal(expectedContents, actualContents);
72
72
  }
73
73
 
74
+ [Fact]
75
+ public async Task DirectBuildFileChangesAreMaintainedWhenPinningTransitiveDependency_DirectoryPackagesPropsIsDiscovered()
76
+ {
77
+ // arrange
78
+ using var tempDir = await TemporaryDirectory.CreateWithContentsAsync(
79
+ [
80
+ ("project.csproj", """
81
+ <Project Sdk="Microsoft.NET.Sdk">
82
+ <PropertyGroup>
83
+ <TargetFramework>net9.0</TargetFramework>
84
+ </PropertyGroup>
85
+ <ItemGroup>
86
+ <PackageReference Include="Completely.Different.Package" />
87
+ <PackageReference Include="Some.Package" />
88
+ </ItemGroup>
89
+ </Project>
90
+ """),
91
+ ("Directory.Packages.props", """
92
+ <Project>
93
+ <PropertyGroup>
94
+ <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
95
+ </PropertyGroup>
96
+ <ItemGroup>
97
+ <PackageVersion Include="Completely.Different.Package" Version="1.0.0" />
98
+ <PackageVersion Include="Some.Package" Version="1.0.0" />
99
+ </ItemGroup>
100
+ </Project>
101
+ """)
102
+ ]);
103
+ var packages = new[]
104
+ {
105
+ MockNuGetPackage.CreateSimplePackage("Completely.Different.Package", "1.0.0", "net9.0"),
106
+ MockNuGetPackage.CreateSimplePackage("Completely.Different.Package", "2.0.0", "net9.0"),
107
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net9.0", [(null, [("Transitive.Package", "1.0.0")])]),
108
+ MockNuGetPackage.CreateSimplePackage("Transitive.Package", "1.0.0", "net9.0"),
109
+ MockNuGetPackage.CreateSimplePackage("Transitive.Package", "2.0.0", "net9.0"),
110
+ };
111
+ await UpdateWorkerTestBase.MockNuGetPackagesInDirectory(packages, tempDir.DirectoryPath);
112
+ var fullProjectPath = Path.Combine(tempDir.DirectoryPath, "project.csproj");
113
+ var fullDirectoryPackagesPath = Path.Combine(tempDir.DirectoryPath, "Directory.Packages.props");
114
+ var buildFiles = new[]
115
+ {
116
+ ProjectBuildFile.Open(tempDir.DirectoryPath, fullProjectPath),
117
+ ProjectBuildFile.Open(tempDir.DirectoryPath, fullDirectoryPackagesPath)
118
+ }.ToImmutableArray();
119
+ var experimentsManager = new ExperimentsManager();
120
+
121
+ // act
122
+ // pin transitive dependency
123
+ var updatedFiles = await PackageReferenceUpdater.UpdateTransitiveDependencyAsync(
124
+ tempDir.DirectoryPath,
125
+ fullProjectPath,
126
+ "Transitive.Package",
127
+ "2.0.0",
128
+ buildFiles,
129
+ experimentsManager,
130
+ new TestLogger());
131
+
132
+ // subsequent update should not overwrite previous change
133
+ PackageReferenceUpdater.TryUpdateDependencyVersion(buildFiles, "Completely.Different.Package", "1.0.0", "2.0.0", new TestLogger());
134
+
135
+ // assert
136
+ foreach (var bf in buildFiles)
137
+ {
138
+ await bf.SaveAsync();
139
+ }
140
+
141
+ var actualProjectContents = await File.ReadAllTextAsync(fullProjectPath, TestContext.Current.CancellationToken);
142
+ var expectedProjectContents = """
143
+ <Project Sdk="Microsoft.NET.Sdk">
144
+ <PropertyGroup>
145
+ <TargetFramework>net9.0</TargetFramework>
146
+ </PropertyGroup>
147
+ <ItemGroup>
148
+ <PackageReference Include="Completely.Different.Package" />
149
+ <PackageReference Include="Some.Package" />
150
+ <PackageReference Include="Transitive.Package" />
151
+ </ItemGroup>
152
+ </Project>
153
+ """;
154
+ Assert.Equal(expectedProjectContents.Replace("\r", ""), actualProjectContents.Replace("\r", ""));
155
+
156
+ var actualDirectoryPackagesContents = await File.ReadAllTextAsync(fullDirectoryPackagesPath, TestContext.Current.CancellationToken);
157
+ var expectedDirectoryPackagesContents = """
158
+ <Project>
159
+ <PropertyGroup>
160
+ <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
161
+ </PropertyGroup>
162
+ <ItemGroup>
163
+ <PackageVersion Include="Completely.Different.Package" Version="2.0.0" />
164
+ <PackageVersion Include="Some.Package" Version="1.0.0" />
165
+ <PackageVersion Include="Transitive.Package" Version="2.0.0" />
166
+ </ItemGroup>
167
+ </Project>
168
+ """;
169
+ Assert.Equal(expectedDirectoryPackagesContents.Replace("\r", ""), actualDirectoryPackagesContents.Replace("\r", ""));
170
+ }
171
+
74
172
  [Theory]
75
173
  [MemberData(nameof(ComputeUpdateOperationsTestData))]
76
174
  public async Task ComputeUpdateOperations
@@ -1852,6 +1852,22 @@ public class MSBuildHelperTests : TestBase
1852
1852
  // expectedError
1853
1853
  new UpdateNotPossible(["Some.Package.1.2.3"]),
1854
1854
  ];
1855
+
1856
+ yield return
1857
+ [
1858
+ // output
1859
+ "Failed to fetch results from V2 feed at 'http://nuget.example.com/FindPackagesById()?id='Some.Package'&semVerLevel=2.0.0' with following message : Response status code does not indicate success: 404.",
1860
+ // expectedError
1861
+ new DependencyNotFound("Some.Package"),
1862
+ ];
1863
+
1864
+ yield return
1865
+ [
1866
+ // output
1867
+ "This part is not reported.\nAn error occurred while reading file '/path/to/packages.config': Some error message.\nThis part is not reported.",
1868
+ // expectedError
1869
+ new DependencyFileNotParseable("/path/to/packages.config", "Some error message."),
1870
+ ];
1855
1871
  }
1856
1872
 
1857
1873
  public static IEnumerable<object[]> GetTopLevelPackageDependencyInfosTestData()