dependabot-nuget 0.290.0 → 0.292.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/.editorconfig +1 -0
  3. data/helpers/lib/NuGetUpdater/Directory.Build.props +1 -0
  4. data/helpers/lib/NuGetUpdater/Directory.Packages.props +1 -1
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +1 -1
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/CloneCommand.cs +1 -1
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +15 -1
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +2 -2
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +1 -1
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +2 -1
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +87 -3
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +1 -1
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +14 -0
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyFinder.cs +2 -0
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementConverter.cs +19 -1
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/BadRequirementException.cs +9 -0
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Clone/CloneWorker.cs +39 -8
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +111 -17
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/PackagesConfigDiscovery.cs +2 -2
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +2 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +19 -11
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ErrorType.cs +2 -0
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +31 -5
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +1 -0
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/BadRequirement.cs +10 -0
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/CommitOptions.cs +1 -1
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyFileNotFound.cs +3 -2
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +1 -2
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobRepoNotFound.cs +1 -4
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PrivateSourceAuthenticationFailure.cs +1 -1
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UnknownError.cs +6 -2
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UpdateNotPossible.cs +1 -1
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +9 -3
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs +3 -2
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +43 -18
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +1 -1
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/WebApplicationTargetsConditionPatcher.cs +12 -1
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DependencyConflictResolver.cs +0 -7
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +48 -17
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +2 -2
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProcessExtensions.cs +45 -7
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProjectHelper.cs +4 -4
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Clone/CloneWorkerTests.cs +60 -2
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +10 -1
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +56 -0
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +41 -0
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +2 -0
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +1 -1
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +2 -1
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +1 -1
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +76 -40
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +20 -2
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +2 -2
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.LockFile.cs +251 -0
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +6 -6
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +63 -5
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +277 -73
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/ProjectHelperTests.cs +65 -0
  59. data/helpers/lib/NuGetUpdater/global.json +1 -1
  60. data/lib/dependabot/nuget/file_fetcher.rb +1 -0
  61. data/lib/dependabot/nuget/file_parser.rb +90 -0
  62. data/lib/dependabot/nuget/language.rb +98 -0
  63. data/lib/dependabot/nuget/native_helpers.rb +25 -0
  64. data/lib/dependabot/nuget/package_manager.rb +51 -0
  65. metadata +12 -6
@@ -1,6 +1,5 @@
1
1
  using NuGet.Versioning;
2
2
 
3
- using NuGetUpdater.Core.Analyze;
4
3
  using NuGetUpdater.Core.Run;
5
4
  using NuGetUpdater.Core.Run.ApiModel;
6
5
  using NuGetUpdater.Core.Test.Utilities;
@@ -228,37 +227,33 @@ public class SerializationTests
228
227
  Assert.False(experimentsManager.UseDirectDiscovery);
229
228
  }
230
229
 
231
- [Fact]
232
- public async Task DeserializeExperimentsManager_UnsupportedJobFileShape_InfoIsReportedAndEmptyExperimentSetIsReturned()
230
+ [Theory]
231
+ [MemberData(nameof(DeserializeErrorTypesData))]
232
+ public void SerializeError(JobErrorBase error, string expectedSerialization)
233
233
  {
234
- // arrange
235
- using var tempDir = new TemporaryDirectory();
236
- var jobFilePath = Path.Combine(tempDir.DirectoryPath, "job.json");
237
- var jobContent = """
238
- {
239
- "this-is-not-a-job-and-parsing-will-fail-but-an-empty-experiment-set-should-sill-be-returned": {
240
- }
241
- }
242
- """;
243
- await File.WriteAllTextAsync(jobFilePath, jobContent);
244
- var capturingTestLogger = new CapturingTestLogger();
245
-
246
- // act - this is the entrypoint the update command uses to parse the job file
247
- var experimentsManager = await ExperimentsManager.FromJobFileAsync(jobFilePath, capturingTestLogger);
234
+ if (error is UnknownError unknown)
235
+ {
236
+ // special case the exception's call stack to make it testable
237
+ unknown.Details["error-backtrace"] = "TEST-BACKTRACE";
238
+ }
248
239
 
249
- // assert
250
- Assert.False(experimentsManager.UseLegacyDependencySolver);
251
- Assert.False(experimentsManager.UseDirectDiscovery);
252
- Assert.Single(capturingTestLogger.Messages, m => m.Contains("Error deserializing job file"));
240
+ var actual = HttpApiHandler.Serialize(error);
241
+ Assert.Equal(expectedSerialization, actual);
253
242
  }
254
243
 
255
244
  [Fact]
256
- public void SerializeError()
245
+ public void SerializeError_AllErrorTypesHaveSerializationTests()
257
246
  {
258
- var error = new JobRepoNotFound("some message");
259
- var actual = HttpApiHandler.Serialize(error);
260
- var expected = """{"data":{"error-type":"job_repo_not_found","error-details":{"message":"some message"}}}""";
261
- Assert.Equal(expected, actual);
247
+ var untestedTypes = typeof(JobErrorBase).Assembly.GetTypes()
248
+ .Where(t => t.IsSubclassOf(typeof(JobErrorBase)))
249
+ .ToHashSet();
250
+ foreach (object?[] data in DeserializeErrorTypesData())
251
+ {
252
+ var testedErrorType = data[0]!.GetType();
253
+ untestedTypes.Remove(testedErrorType);
254
+ }
255
+
256
+ Assert.Empty(untestedTypes.Select(t => t.Name));
262
257
  }
263
258
 
264
259
  [Fact]
@@ -503,7 +498,9 @@ public class SerializationTests
503
498
  "repo": "some/repo"
504
499
  },
505
500
  "commit-message-options": {
506
- "prefix": "[SECURITY] "
501
+ "prefix": "[SECURITY] ",
502
+ "prefix-development": null,
503
+ "include-scope": true
507
504
  }
508
505
  }
509
506
  }
@@ -511,7 +508,58 @@ public class SerializationTests
511
508
  var jobWrapper = RunWorker.Deserialize(jsonWrapperJson)!;
512
509
  Assert.Equal("[SECURITY] ", jobWrapper.Job.CommitMessageOptions!.Prefix);
513
510
  Assert.Null(jobWrapper.Job.CommitMessageOptions!.PrefixDevelopment);
514
- Assert.Null(jobWrapper.Job.CommitMessageOptions!.IncludeScope);
511
+ Assert.True(jobWrapper.Job.CommitMessageOptions!.IncludeScope);
512
+ }
513
+
514
+ public static IEnumerable<object?[]> DeserializeErrorTypesData()
515
+ {
516
+ yield return
517
+ [
518
+ new BadRequirement("some message"),
519
+ """
520
+ {"data":{"error-type":"illformed_requirement","error-details":{"message":"some message"}}}
521
+ """
522
+ ];
523
+
524
+ yield return
525
+ [
526
+ new DependencyFileNotFound("some message", "/some/file"),
527
+ """
528
+ {"data":{"error-type":"dependency_file_not_found","error-details":{"message":"some message","file-path":"/some/file"}}}
529
+ """
530
+ ];
531
+
532
+ yield return
533
+ [
534
+ new JobRepoNotFound("some message"),
535
+ """
536
+ {"data":{"error-type":"job_repo_not_found","error-details":{"message":"some message"}}}
537
+ """
538
+ ];
539
+
540
+ yield return
541
+ [
542
+ new PrivateSourceAuthenticationFailure(["url1", "url2"]),
543
+ """
544
+ {"data":{"error-type":"private_source_authentication_failure","error-details":{"source":"(url1|url2)"}}}
545
+ """
546
+ ];
547
+
548
+ yield return
549
+ [
550
+ new UnknownError(new Exception("some message"), "JOB-ID"),
551
+ """
552
+ {"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"}}}
553
+ """
554
+ ];
555
+
556
+ yield return
557
+ [
558
+ new UpdateNotPossible(["dep1", "dep2"]),
559
+ """
560
+ {"data":{"error-type":"update_not_possible","error-details":{"dependencies":["dep1","dep2"]}}}
561
+ """
562
+ ];
515
563
  }
516
564
 
517
565
  public static IEnumerable<object?[]> DeserializeAllowedUpdatesData()
@@ -596,16 +644,4 @@ public class SerializationTests
596
644
  }
597
645
  ];
598
646
  }
599
-
600
- private class CapturingTestLogger : ILogger
601
- {
602
- private readonly List<string> _messages = new();
603
-
604
- public IReadOnlyList<string> Messages => _messages;
605
-
606
- public void LogRaw(string message)
607
- {
608
- _messages.Add(message);
609
- }
610
- }
611
647
  }
@@ -47,7 +47,7 @@ public abstract class UpdateWorkerTestBase : TestBase
47
47
  {
48
48
  return useSolution
49
49
  ? TestUpdateForSolution(dependencyName, oldVersion, newVersion, projectFiles: [(projectFilePath, projectContents)], projectFilesExpected: [(projectFilePath, expectedProjectContents)], isTransitive, additionalFiles, additionalFilesExpected, packages, experimentsManager)
50
- : TestUpdateForProject(dependencyName, oldVersion, newVersion, projectFile: (projectFilePath, projectContents), expectedProjectContents, isTransitive, additionalFiles, additionalFilesExpected, packages, experimentsManager);
50
+ : TestUpdateForProject(dependencyName, oldVersion, newVersion, projectFile: (projectFilePath, projectContents), expectedProjectContents, isTransitive, additionalFiles, additionalFilesExpected, additionalChecks: null, packages: packages, experimentsManager: experimentsManager);
51
51
  }
52
52
 
53
53
  protected static Task TestUpdate(
@@ -65,7 +65,7 @@ public abstract class UpdateWorkerTestBase : TestBase
65
65
  {
66
66
  return useSolution
67
67
  ? TestUpdateForSolution(dependencyName, oldVersion, newVersion, projectFiles: [projectFile], projectFilesExpected: [(projectFile.Path, expectedProjectContents)], isTransitive, additionalFiles, additionalFilesExpected, packages, experimentsManager)
68
- : TestUpdateForProject(dependencyName, oldVersion, newVersion, projectFile, expectedProjectContents, isTransitive, additionalFiles, additionalFilesExpected, packages, experimentsManager);
68
+ : TestUpdateForProject(dependencyName, oldVersion, newVersion, projectFile, expectedProjectContents, isTransitive, additionalFiles, additionalFilesExpected, additionalChecks: null, packages: packages, experimentsManager: experimentsManager);
69
69
  }
70
70
 
71
71
  protected static Task TestNoChangeforProject(
@@ -101,6 +101,7 @@ public abstract class UpdateWorkerTestBase : TestBase
101
101
  bool isTransitive = false,
102
102
  TestFile[]? additionalFiles = null,
103
103
  TestFile[]? additionalFilesExpected = null,
104
+ Action<string>? additionalChecks = null,
104
105
  MockNuGetPackage[]? packages = null,
105
106
  ExperimentsManager? experimentsManager = null,
106
107
  string projectFilePath = "test-project.csproj",
@@ -114,6 +115,7 @@ public abstract class UpdateWorkerTestBase : TestBase
114
115
  isTransitive,
115
116
  additionalFiles,
116
117
  additionalFilesExpected,
118
+ additionalChecks,
117
119
  packages,
118
120
  experimentsManager,
119
121
  expectedResult);
@@ -127,6 +129,7 @@ public abstract class UpdateWorkerTestBase : TestBase
127
129
  bool isTransitive = false,
128
130
  TestFile[]? additionalFiles = null,
129
131
  TestFile[]? additionalFilesExpected = null,
132
+ Action<string>? additionalChecks = null,
130
133
  MockNuGetPackage[]? packages = null,
131
134
  ExperimentsManager? experimentsManager = null,
132
135
  ExpectedUpdateOperationResult? expectedResult = null)
@@ -156,6 +159,21 @@ public abstract class UpdateWorkerTestBase : TestBase
156
159
  {
157
160
  ValidateUpdateOperationResult(expectedResult, actualResult!);
158
161
  }
162
+ else if ((actualResult.ErrorType ?? ErrorType.None) != ErrorType.None)
163
+ {
164
+ throw new Exception($"Result indicates failure: ErrorType={actualResult.ErrorType}, ErrorDetails={actualResult.ErrorDetails}");
165
+ }
166
+
167
+ if (additionalChecks is not null)
168
+ {
169
+ var sourcesDirectory = temporaryDirectory;
170
+ if (placeFilesInSrc)
171
+ {
172
+ sourcesDirectory = Path.Combine(temporaryDirectory, "src");
173
+ }
174
+
175
+ additionalChecks(sourcesDirectory);
176
+ }
159
177
  });
160
178
 
161
179
  var expectedResultFiles = additionalFilesExpected.Prepend((projectFilePath, expectedProjectContents)).ToArray();
@@ -69,10 +69,10 @@ public partial class UpdateWorkerTests
69
69
  <PropertyGroup>
70
70
  <TargetFramework>netstandard2.0</TargetFramework>
71
71
  </PropertyGroup>
72
-
72
+
73
73
  <ItemGroup>
74
74
  <PackageReference Include="Some.Package" Version="13.0.3" />
75
- </ItemGroup>>
75
+ </ItemGroup>
76
76
  </Project>
77
77
  """,
78
78
  additionalFiles:
@@ -0,0 +1,251 @@
1
+ using Xunit;
2
+
3
+ namespace NuGetUpdater.Core.Test.Update;
4
+
5
+ public partial class UpdateWorkerTests
6
+ {
7
+ public class LockFile : UpdateWorkerTestBase
8
+ {
9
+ [Fact]
10
+ public async Task UpdateSingleDependency()
11
+ {
12
+ await TestUpdateForProject("Some.Package", "1.0.0", "2.0.0",
13
+ packages:
14
+ [
15
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0"),
16
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "2.0.0", "net8.0"),
17
+ ],
18
+ // initial
19
+ projectContents: $"""
20
+ <Project Sdk="Microsoft.NET.Sdk">
21
+ <PropertyGroup>
22
+ <TargetFramework>net8.0</TargetFramework>
23
+ <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
24
+ </PropertyGroup>
25
+
26
+ <ItemGroup>
27
+ <PackageReference Include="Some.Package" Version="1.0.0" />
28
+ </ItemGroup>
29
+ </Project>
30
+ """,
31
+ additionalFiles:
32
+ [
33
+ ("packages.lock.json", "{}")
34
+ ],
35
+ // expected
36
+ expectedProjectContents: $"""
37
+ <Project Sdk="Microsoft.NET.Sdk">
38
+ <PropertyGroup>
39
+ <TargetFramework>net8.0</TargetFramework>
40
+ <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
41
+ </PropertyGroup>
42
+
43
+ <ItemGroup>
44
+ <PackageReference Include="Some.Package" Version="2.0.0" />
45
+ </ItemGroup>
46
+ </Project>
47
+ """,
48
+ additionalChecks: path =>
49
+ {
50
+ var lockContents = File.ReadAllText(Path.Combine(path, "packages.lock.json"));
51
+ Assert.Contains("\"resolved\": \"2.0.0\"", lockContents);
52
+ }
53
+ );
54
+ }
55
+
56
+ [Fact]
57
+ public async Task UpdateSingleDependency_CentralPackageManagement()
58
+ {
59
+ await TestUpdateForProject("Some.Package", "1.0.0", "2.0.0",
60
+ packages:
61
+ [
62
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0"),
63
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "2.0.0", "net8.0"),
64
+ ],
65
+ // initial
66
+ projectContents: $"""
67
+ <Project Sdk="Microsoft.NET.Sdk">
68
+ <PropertyGroup>
69
+ <TargetFramework>net8.0</TargetFramework>
70
+ <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
71
+ </PropertyGroup>
72
+
73
+ <ItemGroup>
74
+ <PackageReference Include="Some.Package" />
75
+ </ItemGroup>
76
+ </Project>
77
+ """,
78
+ additionalFiles:
79
+ [
80
+ ("packages.lock.json", "{}"),
81
+ ("Directory.Packages.props", """
82
+ <Project>
83
+ <PropertyGroup>
84
+ <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
85
+ </PropertyGroup>
86
+
87
+ <ItemGroup>
88
+ <PackageVersion Include="Some.Package" Version="1.0.0" />
89
+ </ItemGroup>
90
+ </Project>
91
+ """)
92
+ ],
93
+ // expected
94
+ expectedProjectContents: $"""
95
+ <Project Sdk="Microsoft.NET.Sdk">
96
+ <PropertyGroup>
97
+ <TargetFramework>net8.0</TargetFramework>
98
+ <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
99
+ </PropertyGroup>
100
+
101
+ <ItemGroup>
102
+ <PackageReference Include="Some.Package" />
103
+ </ItemGroup>
104
+ </Project>
105
+ """,
106
+ additionalFilesExpected:
107
+ [
108
+ ("Directory.Packages.props", """
109
+ <Project>
110
+ <PropertyGroup>
111
+ <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
112
+ </PropertyGroup>
113
+
114
+ <ItemGroup>
115
+ <PackageVersion Include="Some.Package" Version="2.0.0" />
116
+ </ItemGroup>
117
+ </Project>
118
+ """)
119
+ ],
120
+ additionalChecks: path =>
121
+ {
122
+ var lockContents = File.ReadAllText(Path.Combine(path, "packages.lock.json"));
123
+ Assert.Contains("\"resolved\": \"2.0.0\"", lockContents);
124
+ }
125
+ );
126
+ }
127
+
128
+ [Fact]
129
+ public async Task UpdateSingleDependency_WindowsSpecific()
130
+ {
131
+ await TestUpdateForProject("Some.Package", "1.0.0", "2.0.0",
132
+ packages:
133
+ [
134
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0"),
135
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "2.0.0", "net8.0"),
136
+ ],
137
+ // initial
138
+ projectContents: $"""
139
+ <Project Sdk="Microsoft.NET.Sdk">
140
+ <PropertyGroup>
141
+ <TargetFramework>net8.0-windows</TargetFramework>
142
+ <UseWindowsForms>true</UseWindowsForms>
143
+ <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
144
+ </PropertyGroup>
145
+
146
+ <ItemGroup>
147
+ <PackageReference Include="Some.Package" Version="1.0.0" />
148
+ </ItemGroup>
149
+ </Project>
150
+ """,
151
+ additionalFiles:
152
+ [
153
+ ("packages.lock.json", "{}")
154
+ ],
155
+ // expected
156
+ expectedProjectContents: $"""
157
+ <Project Sdk="Microsoft.NET.Sdk">
158
+ <PropertyGroup>
159
+ <TargetFramework>net8.0-windows</TargetFramework>
160
+ <UseWindowsForms>true</UseWindowsForms>
161
+ <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
162
+ </PropertyGroup>
163
+
164
+ <ItemGroup>
165
+ <PackageReference Include="Some.Package" Version="2.0.0" />
166
+ </ItemGroup>
167
+ </Project>
168
+ """,
169
+ additionalChecks: path =>
170
+ {
171
+ var lockContents = File.ReadAllText(Path.Combine(path, "packages.lock.json"));
172
+ Assert.Contains("\"resolved\": \"2.0.0\"", lockContents);
173
+ }
174
+ );
175
+ }
176
+
177
+ [Fact]
178
+ public async Task UpdateSingleDependency_CentralPackageManagement_WindowsSpecific()
179
+ {
180
+ await TestUpdateForProject("Some.Package", "1.0.0", "2.0.0",
181
+ packages:
182
+ [
183
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0"),
184
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "2.0.0", "net8.0"),
185
+ ],
186
+ // initial
187
+ projectContents: $"""
188
+ <Project Sdk="Microsoft.NET.Sdk">
189
+ <PropertyGroup>
190
+ <TargetFramework>net8.0-windows</TargetFramework>
191
+ <UseWindowsForms>true</UseWindowsForms>
192
+ <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
193
+ </PropertyGroup>
194
+
195
+ <ItemGroup>
196
+ <PackageReference Include="Some.Package" />
197
+ </ItemGroup>
198
+ </Project>
199
+ """,
200
+ additionalFiles:
201
+ [
202
+ ("packages.lock.json", "{}"),
203
+ ("Directory.Packages.props", """
204
+ <Project>
205
+ <PropertyGroup>
206
+ <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
207
+ </PropertyGroup>
208
+
209
+ <ItemGroup>
210
+ <PackageVersion Include="Some.Package" Version="1.0.0" />
211
+ </ItemGroup>
212
+ </Project>
213
+ """)
214
+ ],
215
+ // expected
216
+ expectedProjectContents: $"""
217
+ <Project Sdk="Microsoft.NET.Sdk">
218
+ <PropertyGroup>
219
+ <TargetFramework>net8.0-windows</TargetFramework>
220
+ <UseWindowsForms>true</UseWindowsForms>
221
+ <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
222
+ </PropertyGroup>
223
+
224
+ <ItemGroup>
225
+ <PackageReference Include="Some.Package" />
226
+ </ItemGroup>
227
+ </Project>
228
+ """,
229
+ additionalFilesExpected:
230
+ [
231
+ ("Directory.Packages.props", """
232
+ <Project>
233
+ <PropertyGroup>
234
+ <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
235
+ </PropertyGroup>
236
+
237
+ <ItemGroup>
238
+ <PackageVersion Include="Some.Package" Version="2.0.0" />
239
+ </ItemGroup>
240
+ </Project>
241
+ """)
242
+ ],
243
+ additionalChecks: path =>
244
+ {
245
+ var lockContents = File.ReadAllText(Path.Combine(path, "packages.lock.json"));
246
+ Assert.Contains("\"resolved\": \"2.0.0\"", lockContents);
247
+ }
248
+ );
249
+ }
250
+ }
251
+ }
@@ -495,7 +495,7 @@ public partial class UpdateWorkerTests
495
495
  MockNuGetPackage.CreateSimplePackage("Some.Package", "13.0.1", "net8.0"),
496
496
  ],
497
497
  projectContents: $"""
498
- <Project Sdk="Microsoft.NET.Sdk">">
498
+ <Project Sdk="Microsoft.NET.Sdk">
499
499
  <PropertyGroup>
500
500
  <TargetFramework>net8.0</TargetFramework>
501
501
  <SomePackageVersion>9.0.1</SomePackageVersion>
@@ -3075,14 +3075,14 @@ public partial class UpdateWorkerTests
3075
3075
  public async Task UpdatingTransitiveDependencyWithNewSolverCanUpdateJustTheTopLevelPackage()
3076
3076
  {
3077
3077
  // we've been asked to explicitly update a transitive dependency, but we can solve it by updating the top-level package instead
3078
- await TestUpdateForProject("Transitive.Package", "1.0.0", "2.0.0",
3078
+ await TestUpdateForProject("Transitive.Package", "7.0.0", "8.0.0",
3079
3079
  isTransitive: true,
3080
3080
  packages:
3081
3081
  [
3082
- MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0", [("net8.0", [("Transitive.Package", "[1.0.0]")])]),
3083
- MockNuGetPackage.CreateSimplePackage("Some.Package", "2.0.0", "net8.0", [("net8.0", [("Transitive.Package", "[2.0.0]")])]),
3084
- MockNuGetPackage.CreateSimplePackage("Transitive.Package", "1.0.0", "net8.0"),
3085
- MockNuGetPackage.CreateSimplePackage("Transitive.Package", "2.0.0", "net8.0"),
3082
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0", [("net8.0", [("Transitive.Package", "[7.0.0]")])]),
3083
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "2.0.0", "net8.0", [("net8.0", [("Transitive.Package", "[8.0.0]")])]),
3084
+ MockNuGetPackage.CreateSimplePackage("Transitive.Package", "7.0.0", "net8.0"),
3085
+ MockNuGetPackage.CreateSimplePackage("Transitive.Package", "8.0.0", "net8.0"),
3086
3086
  ],
3087
3087
  projectContents: """
3088
3088
  <Project Sdk="Microsoft.NET.Sdk">
@@ -1969,7 +1969,7 @@ public partial class UpdateWorkerTests
1969
1969
  <VSToolsPath Condition="'$(VSToolsPath)' == ''">C:\some\path\that\does\not\exist</VSToolsPath>
1970
1970
  </PropertyGroup>
1971
1971
  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
1972
- <Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
1972
+ <Import Project="$(VSToolsPath)\SomeSubPath\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
1973
1973
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
1974
1974
  Other similar extension points exist, see Microsoft.Common.targets.
1975
1975
  <Target Name="BeforeBuild">
@@ -2050,7 +2050,7 @@ public partial class UpdateWorkerTests
2050
2050
  <VSToolsPath Condition="'$(VSToolsPath)' == ''">C:\some\path\that\does\not\exist</VSToolsPath>
2051
2051
  </PropertyGroup>
2052
2052
  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
2053
- <Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
2053
+ <Import Project="$(VSToolsPath)\SomeSubPath\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
2054
2054
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
2055
2055
  Other similar extension points exist, see Microsoft.Common.targets.
2056
2056
  <Target Name="BeforeBuild">
@@ -2292,15 +2292,73 @@ public partial class UpdateWorkerTests
2292
2292
  }
2293
2293
 
2294
2294
  [Fact]
2295
- public async Task ReportsPrivateSourceAuthenticationFailure()
2295
+ public async Task MissingVisualStudioComponentTargetsAreReportedAsMissingFiles()
2296
2296
  {
2297
- static (int, string) TestHttpHandler(string uriString)
2297
+ using var temporaryDirectory = await TemporaryDirectory.CreateWithContentsAsync(
2298
+ [
2299
+ ("project.csproj", """
2300
+ <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2301
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
2302
+ <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Some.Visual.Studio.Component.props" />
2303
+ <PropertyGroup>
2304
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
2305
+ </PropertyGroup>
2306
+ <ItemGroup>
2307
+ <None Include="packages.config" />
2308
+ </ItemGroup>
2309
+ <ItemGroup>
2310
+ <Reference Include="Some.Package, Version=1.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
2311
+ <HintPath>packages\Some.Package.1.0.0\lib\net45\Some.Package.dll</HintPath>
2312
+ <Private>True</Private>
2313
+ </Reference>
2314
+ </ItemGroup>
2315
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
2316
+ </Project>
2317
+ """),
2318
+ ("packages.config", """
2319
+ <packages>
2320
+ <package id="Some.Package" version="1.0.0" targetFramework="net45" />
2321
+ </packages>
2322
+ """),
2323
+ ("NuGet.Config", """
2324
+ <configuration>
2325
+ <packageSources>
2326
+ <clear />
2327
+ <add key="private_feed" value="packages" />
2328
+ </packageSources>
2329
+ </configuration>
2330
+ """)
2331
+ ]
2332
+ );
2333
+ MockNuGetPackage[] packages =
2334
+ [
2335
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net45"),
2336
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "1.1.0", "net45"),
2337
+ ];
2338
+ await MockNuGetPackagesInDirectory(packages, Path.Combine(temporaryDirectory.DirectoryPath, "packages"));
2339
+ var resultOutputPath = Path.Combine(temporaryDirectory.DirectoryPath, "result.json");
2340
+
2341
+ var worker = new UpdaterWorker(new ExperimentsManager(), new TestLogger());
2342
+ await worker.RunAsync(temporaryDirectory.DirectoryPath, "project.csproj", "Some.Package", "1.0.0", "1.1.0", isTransitive: false, resultOutputPath: resultOutputPath);
2343
+
2344
+ var resultContents = await File.ReadAllTextAsync(resultOutputPath);
2345
+ var result = JsonSerializer.Deserialize<UpdateOperationResult>(resultContents, UpdaterWorker.SerializerOptions)!;
2346
+ Assert.Equal(ErrorType.MissingFile, result.ErrorType);
2347
+ Assert.Equal("$(MSBuildExtensionsPath32)/Microsoft/VisualStudio/v$(VisualStudioVersion)/Some.Visual.Studio.Component.props", result.ErrorDetails!.ToString().NormalizePathToUnix());
2348
+ }
2349
+
2350
+ [Theory]
2351
+ [InlineData(401)]
2352
+ [InlineData(403)]
2353
+ public async Task ReportsPrivateSourceAuthenticationFailure(int httpStatusCode)
2354
+ {
2355
+ (int, string) TestHttpHandler(string uriString)
2298
2356
  {
2299
2357
  var uri = new Uri(uriString, UriKind.Absolute);
2300
2358
  var baseUrl = $"{uri.Scheme}://{uri.Host}:{uri.Port}";
2301
2359
  return uri.PathAndQuery switch
2302
2360
  {
2303
- _ => (401, "{}"), // everything is unauthorized
2361
+ _ => (httpStatusCode, "{}"), // everything is unauthorized
2304
2362
  };
2305
2363
  }
2306
2364
  using var http = TestHttpServer.CreateTestStringServer(TestHttpHandler);