dependabot-nuget 0.321.2 → 0.322.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/Directory.Packages.props +22 -22
  3. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/PackageMapper.cs +9 -0
  4. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Cli/Program.cs +21 -7
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +19 -11
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/CloneCommand.cs +19 -9
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +21 -14
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/FrameworkCheckCommand.cs +8 -5
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +29 -16
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +20 -19
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Program.cs +2 -1
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencyDiscoveryTargetingPacks.props +2 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencySolver/IDependencySolver.cs +8 -0
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencySolver/MSBuildDependencySolver.cs +32 -0
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +1 -0
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +10 -1
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/WorkspaceDiscoveryResult.cs +6 -0
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +3 -0
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/GlobalJsonBuildFile.cs +5 -13
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/PrivateSourceTimedOutException.cs +12 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +4 -0
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PrivateSourceTimedOut.cs +10 -0
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestTextGenerator.cs +1 -1
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/CreateSecurityUpdatePullRequestHandler.cs +1 -1
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/GroupUpdateAllVersionsHandler.cs +2 -2
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshGroupUpdatePullRequestHandler.cs +1 -1
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshSecurityUpdatePullRequestHandler.cs +1 -1
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshVersionUpdatePullRequestHandler.cs +1 -1
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/DotNetToolsJsonUpdater.cs +6 -3
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/FileWriters/FileWriterWorker.cs +376 -0
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/FileWriters/IFileWriter.cs +14 -0
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/FileWriters/XmlFileWriter.cs +477 -0
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/GlobalJsonUpdater.cs +9 -5
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateOperationBase.cs +18 -7
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +26 -1
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +15 -0
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/DependencySolver/MSBuildDependencySolverTests.cs +633 -0
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.GlobalJson.cs +0 -2
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +0 -2
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +49 -0
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/GlobalJsonBuildFileTests.cs +0 -1
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/EndToEndTests.cs +484 -0
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/HttpApiHandlerTests.cs +1 -0
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/JobErrorBaseTests.cs +7 -0
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MessageReportTests.cs +11 -0
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestTextTests.cs +21 -22
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +1 -1
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +8 -0
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/DotNetToolsJsonUpdaterTests.cs +181 -0
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/FileWriterTestsBase.cs +61 -0
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/FileWriterWorkerTests.cs +917 -0
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/FileWriterWorkerTests_MiscellaneousTests.cs +154 -0
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/TestFileWriterReturnsConstantResult.cs +20 -0
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/XmlFileWriterTests.cs +1620 -0
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/XmlFileWriterTests_CreateUpdatedVersionRangeTests.cs +25 -0
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/GlobalJsonUpdaterTests.cs +139 -0
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs +1961 -1
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateOperationResultTests.cs +116 -0
  59. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +16 -1043
  60. data/helpers/lib/NuGetUpdater/global.json +1 -1
  61. metadata +21 -10
  62. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DotNetTools.cs +0 -375
  63. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +0 -296
  64. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.LockFile.cs +0 -251
  65. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +0 -201
  66. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +0 -3821
  67. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +0 -2706
@@ -0,0 +1,917 @@
1
+ using System.Text.Json;
2
+
3
+ using NuGet.Versioning;
4
+
5
+ using NuGetUpdater.Core.DependencySolver;
6
+ using NuGetUpdater.Core.Discover;
7
+ using NuGetUpdater.Core.Run;
8
+ using NuGetUpdater.Core.Test.Utilities;
9
+ using NuGetUpdater.Core.Updater;
10
+ using NuGetUpdater.Core.Updater.FileWriters;
11
+
12
+ using Xunit;
13
+
14
+ namespace NuGetUpdater.Core.Test.Update.FileWriters;
15
+
16
+ public class FileWriterWorkerTests : TestBase
17
+ {
18
+ [Fact]
19
+ public async Task EndToEnd_ProjectReference()
20
+ {
21
+ // project is directly changed
22
+ await TestAsync(
23
+ dependencyName: "Some.Dependency",
24
+ oldDependencyVersion: "1.0.0",
25
+ newDependencyVersion: "2.0.0",
26
+ projectContents: """
27
+ <Project Sdk="Microsoft.NET.Sdk">
28
+ <PropertyGroup>
29
+ <TargetFramework>net9.0</TargetFramework>
30
+ </PropertyGroup>
31
+ <ItemGroup>
32
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
33
+ </ItemGroup>
34
+ </Project>
35
+ """,
36
+ additionalFiles: [],
37
+ packages: [
38
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "1.0.0", "net9.0"),
39
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "2.0.0", "net9.0"),
40
+ ],
41
+ discoveryWorker: null, // use real worker
42
+ dependencySolver: null, // use real worker
43
+ fileWriter: null, // use real worker
44
+ expectedProjectContents: """
45
+ <Project Sdk="Microsoft.NET.Sdk">
46
+ <PropertyGroup>
47
+ <TargetFramework>net9.0</TargetFramework>
48
+ </PropertyGroup>
49
+ <ItemGroup>
50
+ <PackageReference Include="Some.Dependency" Version="2.0.0" />
51
+ </ItemGroup>
52
+ </Project>
53
+ """,
54
+ expectedAdditionalFiles: [],
55
+ expectedOperations: [
56
+ new DirectUpdate() { DependencyName = "Some.Dependency", NewVersion = NuGetVersion.Parse("2.0.0"), UpdatedFiles = ["/project.csproj"] }
57
+ ]
58
+ );
59
+ }
60
+
61
+ [Fact]
62
+ public async Task EndToEnd_ProjectReference_ThroughProjectReferences()
63
+ {
64
+ // project is directly changed
65
+ await TestAsync(
66
+ dependencyName: "Some.Dependency",
67
+ oldDependencyVersion: "1.0.0",
68
+ newDependencyVersion: "2.0.0",
69
+ projectName: "src/project.csproj",
70
+ projectContents: """
71
+ <Project Sdk="Microsoft.NET.Sdk">
72
+ <PropertyGroup>
73
+ <TargetFramework>net9.0</TargetFramework>
74
+ </PropertyGroup>
75
+ <ItemGroup>
76
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
77
+ </ItemGroup>
78
+ <ItemGroup>
79
+ <ProjectReference Include="..\common\common.csproj" />
80
+ </ItemGroup>
81
+ </Project>
82
+ """,
83
+ additionalFiles: [
84
+ ("common/common.csproj", """
85
+ <Project Sdk="Microsoft.NET.Sdk">
86
+ <PropertyGroup>
87
+ <TargetFramework>net9.0</TargetFramework>
88
+ </PropertyGroup>
89
+ <ItemGroup>
90
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
91
+ </ItemGroup>
92
+ </Project>
93
+ """)
94
+ ],
95
+ packages: [
96
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "1.0.0", "net9.0"),
97
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "2.0.0", "net9.0"),
98
+ ],
99
+ discoveryWorker: null, // use real worker
100
+ dependencySolver: null, // use real worker
101
+ fileWriter: null, // use real worker
102
+ expectedProjectContents: """
103
+ <Project Sdk="Microsoft.NET.Sdk">
104
+ <PropertyGroup>
105
+ <TargetFramework>net9.0</TargetFramework>
106
+ </PropertyGroup>
107
+ <ItemGroup>
108
+ <PackageReference Include="Some.Dependency" Version="2.0.0" />
109
+ </ItemGroup>
110
+ <ItemGroup>
111
+ <ProjectReference Include="..\common\common.csproj" />
112
+ </ItemGroup>
113
+ </Project>
114
+ """,
115
+ expectedAdditionalFiles: [
116
+ ("common/common.csproj", """
117
+ <Project Sdk="Microsoft.NET.Sdk">
118
+ <PropertyGroup>
119
+ <TargetFramework>net9.0</TargetFramework>
120
+ </PropertyGroup>
121
+ <ItemGroup>
122
+ <PackageReference Include="Some.Dependency" Version="2.0.0" />
123
+ </ItemGroup>
124
+ </Project>
125
+ """)
126
+ ],
127
+ expectedOperations: [
128
+ new DirectUpdate() { DependencyName = "Some.Dependency", NewVersion = NuGetVersion.Parse("2.0.0"), UpdatedFiles = ["/common/common.csproj", "/src/project.csproj"] }
129
+ ]
130
+ );
131
+ }
132
+
133
+ [Fact]
134
+ public async Task EndToEnd_ProjectReference_NotInRoot()
135
+ {
136
+ // project is directly changed
137
+ await TestAsync(
138
+ dependencyName: "Some.Dependency",
139
+ oldDependencyVersion: "1.0.0",
140
+ newDependencyVersion: "2.0.0",
141
+ projectName: "src/project.csproj",
142
+ projectContents: """
143
+ <Project Sdk="Microsoft.NET.Sdk">
144
+ <Import Project="Misc.props" />
145
+ <PropertyGroup>
146
+ <TargetFramework>net9.0</TargetFramework>
147
+ </PropertyGroup>
148
+ <ItemGroup>
149
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
150
+ </ItemGroup>
151
+ </Project>
152
+ """,
153
+ additionalFiles: [
154
+ ("src/Misc.props", """
155
+ <Project />
156
+ """)
157
+ ],
158
+ packages: [
159
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "1.0.0", "net9.0"),
160
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "2.0.0", "net9.0"),
161
+ ],
162
+ discoveryWorker: null, // use real worker
163
+ dependencySolver: null, // use real worker
164
+ fileWriter: null, // use real worker
165
+ expectedProjectContents: """
166
+ <Project Sdk="Microsoft.NET.Sdk">
167
+ <Import Project="Misc.props" />
168
+ <PropertyGroup>
169
+ <TargetFramework>net9.0</TargetFramework>
170
+ </PropertyGroup>
171
+ <ItemGroup>
172
+ <PackageReference Include="Some.Dependency" Version="2.0.0" />
173
+ </ItemGroup>
174
+ </Project>
175
+ """,
176
+ expectedAdditionalFiles: [],
177
+ expectedOperations: [
178
+ new DirectUpdate() { DependencyName = "Some.Dependency", NewVersion = NuGetVersion.Parse("2.0.0"), UpdatedFiles = ["/src/project.csproj"] }
179
+ ]
180
+ );
181
+ }
182
+
183
+ [Fact]
184
+ public async Task EndToEnd_ProjectReferenceWithPackageLockJson()
185
+ {
186
+ // project is directly changed
187
+ await TestAsync(
188
+ dependencyName: "Some.Dependency",
189
+ oldDependencyVersion: "1.0.0",
190
+ newDependencyVersion: "2.0.0",
191
+ projectContents: """
192
+ <Project Sdk="Microsoft.NET.Sdk">
193
+ <PropertyGroup>
194
+ <TargetFramework>net9.0</TargetFramework>
195
+ </PropertyGroup>
196
+ <ItemGroup>
197
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
198
+ </ItemGroup>
199
+ </Project>
200
+ """,
201
+ additionalFiles: [
202
+ ("packages.lock.json", "{}")
203
+ ],
204
+ packages: [
205
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "1.0.0", "net9.0"),
206
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "2.0.0", "net9.0"),
207
+ ],
208
+ discoveryWorker: null, // use real worker
209
+ dependencySolver: null, // use real worker
210
+ fileWriter: null, // use real worker
211
+ expectedProjectContents: """
212
+ <Project Sdk="Microsoft.NET.Sdk">
213
+ <PropertyGroup>
214
+ <TargetFramework>net9.0</TargetFramework>
215
+ </PropertyGroup>
216
+ <ItemGroup>
217
+ <PackageReference Include="Some.Dependency" Version="2.0.0" />
218
+ </ItemGroup>
219
+ </Project>
220
+ """,
221
+ expectedAdditionalFiles: [],
222
+ expectedOperations: [
223
+ new DirectUpdate() { DependencyName = "Some.Dependency", NewVersion = NuGetVersion.Parse("2.0.0"), UpdatedFiles = ["/project.csproj"] }
224
+ ],
225
+ additionalChecks: (repoContentsPath) =>
226
+ {
227
+ var lockFilePath = Path.Join(repoContentsPath.FullName, "packages.lock.json");
228
+ var lockFileContent = File.ReadAllText(lockFilePath);
229
+ Assert.Contains("\"resolved\": \"2.0.0\"", lockFileContent);
230
+ }
231
+ );
232
+ }
233
+
234
+ [Fact]
235
+ public async Task EndToEnd_ProjectReference_PinnedTransitiveDependency()
236
+ {
237
+ // project is directly changed
238
+ await TestAsync(
239
+ dependencyName: "Transitive.Dependency",
240
+ oldDependencyVersion: "2.0.0",
241
+ newDependencyVersion: "3.0.0",
242
+ projectContents: """
243
+ <Project Sdk="Microsoft.NET.Sdk">
244
+ <PropertyGroup>
245
+ <TargetFramework>net9.0</TargetFramework>
246
+ </PropertyGroup>
247
+ <ItemGroup>
248
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
249
+ </ItemGroup>
250
+ </Project>
251
+ """,
252
+ additionalFiles: [],
253
+ packages: [
254
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "1.0.0", "net9.0", [(null, [("Transitive.Dependency", "2.0.0")])]),
255
+ MockNuGetPackage.CreateSimplePackage("Transitive.Dependency", "2.0.0", "net9.0"),
256
+ MockNuGetPackage.CreateSimplePackage("Transitive.Dependency", "3.0.0", "net9.0"),
257
+ ],
258
+ discoveryWorker: null, // use real worker
259
+ dependencySolver: null, // use real worker
260
+ fileWriter: null, // use real worker
261
+ expectedProjectContents: """
262
+ <Project Sdk="Microsoft.NET.Sdk">
263
+ <PropertyGroup>
264
+ <TargetFramework>net9.0</TargetFramework>
265
+ </PropertyGroup>
266
+ <ItemGroup>
267
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
268
+ <PackageReference Include="Transitive.Dependency" Version="3.0.0" />
269
+ </ItemGroup>
270
+ </Project>
271
+ """,
272
+ expectedAdditionalFiles: [],
273
+ expectedOperations: [
274
+ new PinnedUpdate() { DependencyName = "Transitive.Dependency", NewVersion = NuGetVersion.Parse("3.0.0"), UpdatedFiles = ["/project.csproj"] }
275
+ ]
276
+ );
277
+ }
278
+
279
+ [Fact]
280
+ public async Task EndToEnd_ProjectReference_PinnedTransitiveDependency_With_CentralPackageTransitivePinningEnabled()
281
+ {
282
+ // project is directly changed
283
+ await TestAsync(
284
+ dependencyName: "Transitive.Dependency",
285
+ oldDependencyVersion: "2.0.0",
286
+ newDependencyVersion: "3.0.0",
287
+ projectContents: """
288
+ <Project Sdk="Microsoft.NET.Sdk">
289
+ <PropertyGroup>
290
+ <TargetFramework>net9.0</TargetFramework>
291
+ </PropertyGroup>
292
+ <ItemGroup>
293
+ <PackageReference Include="Some.Dependency" />
294
+ </ItemGroup>
295
+ </Project>
296
+ """,
297
+ additionalFiles: [
298
+ ("Directory.Build.props", "<Project />"),
299
+ ("Directory.Build.targets", "<Project />"),
300
+ ("Directory.Packages.props", """
301
+ <Project>
302
+ <PropertyGroup>
303
+ <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
304
+ <CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
305
+ </PropertyGroup>
306
+ <ItemGroup>
307
+ <PackageVersion Include="Some.Dependency" Version="1.0.0" />
308
+ </ItemGroup>
309
+ </Project>
310
+ """)
311
+ ],
312
+ packages: [
313
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "1.0.0", "net9.0", [(null, [("Transitive.Dependency", "2.0.0")])]),
314
+ MockNuGetPackage.CreateSimplePackage("Transitive.Dependency", "2.0.0", "net9.0"),
315
+ MockNuGetPackage.CreateSimplePackage("Transitive.Dependency", "3.0.0", "net9.0"),
316
+ ],
317
+ discoveryWorker: null, // use real worker
318
+ dependencySolver: null, // use real worker
319
+ fileWriter: null, // use real worker
320
+ expectedProjectContents: """
321
+ <Project Sdk="Microsoft.NET.Sdk">
322
+ <PropertyGroup>
323
+ <TargetFramework>net9.0</TargetFramework>
324
+ </PropertyGroup>
325
+ <ItemGroup>
326
+ <PackageReference Include="Some.Dependency" />
327
+ </ItemGroup>
328
+ </Project>
329
+ """,
330
+ expectedAdditionalFiles: [
331
+ ("Directory.Packages.props", """
332
+ <Project>
333
+ <PropertyGroup>
334
+ <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
335
+ <CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
336
+ </PropertyGroup>
337
+ <ItemGroup>
338
+ <PackageVersion Include="Some.Dependency" Version="1.0.0" />
339
+ <PackageVersion Include="Transitive.Dependency" Version="3.0.0" />
340
+ </ItemGroup>
341
+ </Project>
342
+ """)
343
+ ],
344
+ expectedOperations: [
345
+ new PinnedUpdate() { DependencyName = "Transitive.Dependency", NewVersion = NuGetVersion.Parse("3.0.0"), UpdatedFiles = ["/Directory.Packages.props"] }
346
+ ]
347
+ );
348
+ }
349
+
350
+ [Fact]
351
+ public async Task EndToEnd_PackagesConfig()
352
+ {
353
+ // project is directly changed
354
+ await TestAsync(
355
+ dependencyName: "Some.Dependency",
356
+ oldDependencyVersion: "1.0.0",
357
+ newDependencyVersion: "2.0.0",
358
+ projectContents: """
359
+ <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
360
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
361
+ <PropertyGroup>
362
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
363
+ </PropertyGroup>
364
+ <ItemGroup>
365
+ <None Include="packages.config" />
366
+ </ItemGroup>
367
+ <ItemGroup>
368
+ <Reference Include="Some.Dependency">
369
+ <HintPath>packages\Some.Dependency.1.0.0\lib\net45\Some.Dependency.dll</HintPath>
370
+ <Private>True</Private>
371
+ </Reference>
372
+ </ItemGroup>
373
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
374
+ </Project>
375
+ """,
376
+ additionalFiles: [
377
+ ("packages.config", """
378
+ <?xml version="1.0" encoding="utf-8"?>
379
+ <packages>
380
+ <package id="Some.Dependency" version="1.0.0" targetFramework="net45" />
381
+ </packages>
382
+ """)
383
+ ],
384
+ packages: [
385
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "1.0.0", "net45"),
386
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "2.0.0", "net45"),
387
+ ],
388
+ discoveryWorker: null, // use real worker
389
+ dependencySolver: null, // use real worker
390
+ fileWriter: null, // use real worker
391
+ expectedProjectContents: """
392
+ <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
393
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
394
+ <PropertyGroup>
395
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
396
+ </PropertyGroup>
397
+ <ItemGroup>
398
+ <None Include="packages.config" />
399
+ </ItemGroup>
400
+ <ItemGroup>
401
+ <Reference Include="Some.Dependency">
402
+ <HintPath>packages\Some.Dependency.2.0.0\lib\net45\Some.Dependency.dll</HintPath>
403
+ <Private>True</Private>
404
+ </Reference>
405
+ </ItemGroup>
406
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
407
+ </Project>
408
+ """,
409
+ expectedAdditionalFiles: [
410
+ ("packages.config", """
411
+ <?xml version="1.0" encoding="utf-8"?>
412
+ <packages>
413
+ <package id="Some.Dependency" version="2.0.0" targetFramework="net45" />
414
+ </packages>
415
+ """)
416
+ ],
417
+ expectedOperations: [
418
+ new DirectUpdate() { DependencyName = "Some.Dependency", NewVersion = NuGetVersion.Parse("2.0.0"), UpdatedFiles = ["/packages.config", "/project.csproj"] }
419
+ ]
420
+ );
421
+ }
422
+
423
+ [Fact]
424
+ public async Task EndToEnd_PackagesConfig_AndPackageReference()
425
+ {
426
+ // project is directly changed
427
+ await TestAsync(
428
+ dependencyName: "Some.Dependency",
429
+ oldDependencyVersion: "1.0.0",
430
+ newDependencyVersion: "2.0.0",
431
+ projectContents: """
432
+ <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
433
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
434
+ <PropertyGroup>
435
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
436
+ </PropertyGroup>
437
+ <ItemGroup>
438
+ <None Include="packages.config" />
439
+ </ItemGroup>
440
+ <ItemGroup>
441
+ <Reference Include="Some.Dependency">
442
+ <HintPath>packages\Some.Dependency.1.0.0\lib\net45\Some.Dependency.dll</HintPath>
443
+ <Private>True</Private>
444
+ </Reference>
445
+ </ItemGroup>
446
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
447
+ </Project>
448
+ """,
449
+ additionalFiles: [
450
+ ("packages.config", """
451
+ <?xml version="1.0" encoding="utf-8"?>
452
+ <packages>
453
+ <package id="Some.Dependency" version="1.0.0" targetFramework="net45" />
454
+ </packages>
455
+ """),
456
+ ("Directory.Build.props", """
457
+ <Project>
458
+ <ItemGroup>
459
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
460
+ </ItemGroup>
461
+ </Project>
462
+ """)
463
+ ],
464
+ packages: [
465
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "1.0.0", "net45"),
466
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "2.0.0", "net45"),
467
+ ],
468
+ discoveryWorker: null, // use real worker
469
+ dependencySolver: null, // use real worker
470
+ fileWriter: null, // use real worker
471
+ expectedProjectContents: """
472
+ <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
473
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
474
+ <PropertyGroup>
475
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
476
+ </PropertyGroup>
477
+ <ItemGroup>
478
+ <None Include="packages.config" />
479
+ </ItemGroup>
480
+ <ItemGroup>
481
+ <Reference Include="Some.Dependency">
482
+ <HintPath>packages\Some.Dependency.2.0.0\lib\net45\Some.Dependency.dll</HintPath>
483
+ <Private>True</Private>
484
+ </Reference>
485
+ </ItemGroup>
486
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
487
+ </Project>
488
+ """,
489
+ expectedAdditionalFiles: [
490
+ ("packages.config", """
491
+ <?xml version="1.0" encoding="utf-8"?>
492
+ <packages>
493
+ <package id="Some.Dependency" version="2.0.0" targetFramework="net45" />
494
+ </packages>
495
+ """),
496
+ ("Directory.Build.props", """
497
+ <Project>
498
+ <ItemGroup>
499
+ <PackageReference Include="Some.Dependency" Version="2.0.0" />
500
+ </ItemGroup>
501
+ </Project>
502
+ """)
503
+ ],
504
+ expectedOperations: [
505
+ new DirectUpdate() { DependencyName = "Some.Dependency", NewVersion = NuGetVersion.Parse("2.0.0"), UpdatedFiles = ["/Directory.Build.props", "/packages.config", "/project.csproj"] }
506
+ ]
507
+ );
508
+ }
509
+
510
+ [Fact]
511
+ public async Task EndToEnd_DotNetTools_UpdatePerformed()
512
+ {
513
+ // project is unchanged but `.config/dotnet-tools.json` is updated
514
+ await TestAsync(
515
+ dependencyName: "Some.DotNet.Tool",
516
+ oldDependencyVersion: "1.0.0",
517
+ newDependencyVersion: "1.1.0",
518
+ projectContents: """
519
+ <Project Sdk="Microsoft.NET.Sdk">
520
+ <PropertyGroup>
521
+ <TargetFramework>net9.0</TargetFramework>
522
+ </PropertyGroup>
523
+ <ItemGroup>
524
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
525
+ </ItemGroup>
526
+ </Project>
527
+ """,
528
+ additionalFiles: [
529
+ (".config/dotnet-tools.json", """
530
+ {
531
+ "version": 1,
532
+ "isRoot": true,
533
+ "tools": {
534
+ "some.dotnet.tool": {
535
+ "version": "1.0.0",
536
+ "commands": [
537
+ "some.dotnet.tool"
538
+ ]
539
+ },
540
+ "some-other-tool": {
541
+ "version": "2.1.3",
542
+ "commands": [
543
+ "some-other-tool"
544
+ ]
545
+ }
546
+ }
547
+ }
548
+ """)
549
+ ],
550
+ packages: [
551
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "1.0.0", "net9.0"),
552
+ MockNuGetPackage.CreateDotNetToolPackage("Some.DotNet.Tool", "1.0.0", "net9.0"),
553
+ MockNuGetPackage.CreateDotNetToolPackage("Some.DotNet.Tool", "1.1.0", "net9.0"),
554
+ ],
555
+ discoveryWorker: null, // use real worker
556
+ dependencySolver: null, // use real worker
557
+ fileWriter: null, // use real worker
558
+ expectedProjectContents: """
559
+ <Project Sdk="Microsoft.NET.Sdk">
560
+ <PropertyGroup>
561
+ <TargetFramework>net9.0</TargetFramework>
562
+ </PropertyGroup>
563
+ <ItemGroup>
564
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
565
+ </ItemGroup>
566
+ </Project>
567
+ """,
568
+ expectedAdditionalFiles: [
569
+ (".config/dotnet-tools.json", """
570
+ {
571
+ "version": 1,
572
+ "isRoot": true,
573
+ "tools": {
574
+ "some.dotnet.tool": {
575
+ "version": "1.1.0",
576
+ "commands": [
577
+ "some.dotnet.tool"
578
+ ]
579
+ },
580
+ "some-other-tool": {
581
+ "version": "2.1.3",
582
+ "commands": [
583
+ "some-other-tool"
584
+ ]
585
+ }
586
+ }
587
+ }
588
+ """)],
589
+ expectedOperations: [
590
+ new DirectUpdate() { DependencyName = "Some.DotNet.Tool", OldVersion = NuGetVersion.Parse("1.0.0"), NewVersion = NuGetVersion.Parse("1.1.0"), UpdatedFiles = ["/.config/dotnet-tools.json"] }
591
+ ]
592
+ );
593
+ }
594
+
595
+ [Fact]
596
+ public async Task EndToEnd_DotNetTools_NoUpdatePerformed()
597
+ {
598
+ // `.config/dotnet-tools.json` doesn't have the requested tool, so no update is performed
599
+ await TestAsync(
600
+ dependencyName: "Some.DotNet.Tool",
601
+ oldDependencyVersion: "1.0.0",
602
+ newDependencyVersion: "1.1.0",
603
+ projectContents: """
604
+ <Project Sdk="Microsoft.NET.Sdk">
605
+ <PropertyGroup>
606
+ <TargetFramework>net9.0</TargetFramework>
607
+ </PropertyGroup>
608
+ <ItemGroup>
609
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
610
+ </ItemGroup>
611
+ </Project>
612
+ """,
613
+ additionalFiles: [
614
+ (".config/dotnet-tools.json", """
615
+ {
616
+ "version": 1,
617
+ "isRoot": true,
618
+ "tools": {
619
+ "some-other-tool": {
620
+ "version": "2.1.3",
621
+ "commands": [
622
+ "some-other-tool"
623
+ ]
624
+ }
625
+ }
626
+ }
627
+ """)
628
+ ],
629
+ packages: [
630
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "1.0.0", "net9.0"),
631
+ MockNuGetPackage.CreateDotNetToolPackage("Some.DotNet.Tool", "1.0.0", "net9.0"),
632
+ MockNuGetPackage.CreateDotNetToolPackage("Some.DotNet.Tool", "1.1.0", "net9.0"),
633
+ ],
634
+ discoveryWorker: null, // use real worker
635
+ dependencySolver: null, // use real worker
636
+ fileWriter: null, // use real worker
637
+ expectedProjectContents: """
638
+ <Project Sdk="Microsoft.NET.Sdk">
639
+ <PropertyGroup>
640
+ <TargetFramework>net9.0</TargetFramework>
641
+ </PropertyGroup>
642
+ <ItemGroup>
643
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
644
+ </ItemGroup>
645
+ </Project>
646
+ """,
647
+ expectedAdditionalFiles: [
648
+ (".config/dotnet-tools.json", """
649
+ {
650
+ "version": 1,
651
+ "isRoot": true,
652
+ "tools": {
653
+ "some-other-tool": {
654
+ "version": "2.1.3",
655
+ "commands": [
656
+ "some-other-tool"
657
+ ]
658
+ }
659
+ }
660
+ }
661
+ """)],
662
+ expectedOperations: []
663
+ );
664
+ }
665
+
666
+ [Fact]
667
+ public async Task EndToEnd_GlobalJson()
668
+ {
669
+ // project is unchanged but `global.json` is updated
670
+ await TestAsync(
671
+ dependencyName: "Some.MSBuild.Sdk",
672
+ oldDependencyVersion: "1.0.0",
673
+ newDependencyVersion: "1.1.0",
674
+ projectContents: """
675
+ <Project Sdk="Microsoft.NET.Sdk">
676
+ <PropertyGroup>
677
+ <TargetFramework>net9.0</TargetFramework>
678
+ </PropertyGroup>
679
+ <ItemGroup>
680
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
681
+ </ItemGroup>
682
+ </Project>
683
+ """,
684
+ additionalFiles: [
685
+ ("global.json", """
686
+ {
687
+ "sdk": {
688
+ "version": "6.0.405",
689
+ "rollForward": "latestPatch"
690
+ },
691
+ "msbuild-sdks": {
692
+ "Some.MSBuild.Sdk": "1.0.0"
693
+ }
694
+ }
695
+ """)
696
+ ],
697
+ packages: [
698
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "1.0.0", "net9.0"),
699
+ MockNuGetPackage.CreateMSBuildSdkPackage("Some.MSBuild.Sdk", "1.0.0"),
700
+ MockNuGetPackage.CreateMSBuildSdkPackage("Some.MSBuild.Sdk", "1.1.0"),
701
+ ],
702
+ discoveryWorker: null, // use real worker
703
+ dependencySolver: null, // use real worker
704
+ fileWriter: null, // use real worker
705
+ expectedProjectContents: """
706
+ <Project Sdk="Microsoft.NET.Sdk">
707
+ <PropertyGroup>
708
+ <TargetFramework>net9.0</TargetFramework>
709
+ </PropertyGroup>
710
+ <ItemGroup>
711
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
712
+ </ItemGroup>
713
+ </Project>
714
+ """,
715
+ expectedAdditionalFiles: [
716
+ ("global.json", """
717
+ {
718
+ "sdk": {
719
+ "version": "6.0.405",
720
+ "rollForward": "latestPatch"
721
+ },
722
+ "msbuild-sdks": {
723
+ "Some.MSBuild.Sdk": "1.1.0"
724
+ }
725
+ }
726
+ """)],
727
+ expectedOperations: [
728
+ new DirectUpdate() { DependencyName = "Some.MSBuild.Sdk", OldVersion = NuGetVersion.Parse("1.0.0"), NewVersion = NuGetVersion.Parse("1.1.0"), UpdatedFiles = ["/global.json"] }
729
+ ]
730
+ );
731
+ }
732
+
733
+ [Fact]
734
+ public async Task EndToEnd_FileEditUnsuccessful_NoFileEditsRetained()
735
+ {
736
+ // the file writer has been interrupted to make no edits and report failure
737
+ await TestAsync(
738
+ dependencyName: "Some.Dependency",
739
+ oldDependencyVersion: "1.0.0",
740
+ newDependencyVersion: "2.0.0",
741
+ projectContents: """
742
+ <Project Sdk="Microsoft.NET.Sdk">
743
+ <PropertyGroup>
744
+ <TargetFramework>net9.0</TargetFramework>
745
+ </PropertyGroup>
746
+ <ItemGroup>
747
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
748
+ </ItemGroup>
749
+ </Project>
750
+ """,
751
+ additionalFiles: [],
752
+ packages: [
753
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "1.0.0", "net9.0"),
754
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "2.0.0", "net9.0"),
755
+ ],
756
+ discoveryWorker: null, // use real worker
757
+ dependencySolver: null, // use real worker
758
+ fileWriter: new TestFileWriterReturnsConstantResult(false), // always report failure to edit files
759
+ expectedProjectContents: """
760
+ <Project Sdk="Microsoft.NET.Sdk">
761
+ <PropertyGroup>
762
+ <TargetFramework>net9.0</TargetFramework>
763
+ </PropertyGroup>
764
+ <ItemGroup>
765
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
766
+ </ItemGroup>
767
+ </Project>
768
+ """,
769
+ expectedAdditionalFiles: [],
770
+ expectedOperations: []
771
+ );
772
+ }
773
+
774
+ [Fact]
775
+ public async Task EndToEnd_FinalDependencyResolutionUnsuccessful_NoFileEditsRetained()
776
+ {
777
+ // the discovery worker has been interrupted to report the update didn't produce the desired result
778
+ // no file edits will be preserved
779
+ var discoveryRequestCount = 0;
780
+ await TestAsync(
781
+ dependencyName: "Some.Dependency",
782
+ oldDependencyVersion: "1.0.0",
783
+ newDependencyVersion: "2.0.0",
784
+ projectContents: """
785
+ <Project Sdk="Microsoft.NET.Sdk">
786
+ <PropertyGroup>
787
+ <TargetFramework>net9.0</TargetFramework>
788
+ </PropertyGroup>
789
+ <ItemGroup>
790
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
791
+ </ItemGroup>
792
+ </Project>
793
+ """,
794
+ additionalFiles: [],
795
+ packages: [
796
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "1.0.0", "net9.0"),
797
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "2.0.0", "net9.0"),
798
+ ],
799
+ discoveryWorker: new TestDiscoveryWorker(args =>
800
+ {
801
+ discoveryRequestCount++;
802
+ var result = discoveryRequestCount switch
803
+ {
804
+ // initial request, report 1.0.0
805
+ 1 => new WorkspaceDiscoveryResult()
806
+ {
807
+ Path = "/",
808
+ Projects = [
809
+ new ProjectDiscoveryResult()
810
+ {
811
+ FilePath = "project.csproj",
812
+ Dependencies = [new Dependency("Some.Dependency", "1.0.0", DependencyType.PackageReference)],
813
+ TargetFrameworks = ["net9.0"],
814
+ AdditionalFiles = [],
815
+ ImportedFiles = [],
816
+ ReferencedProjectPaths = []
817
+ }
818
+ ]
819
+ },
820
+ // post-edit request, report 1.0.0 again, indicating the file edits didn't produce the desired result
821
+ 2 => new WorkspaceDiscoveryResult()
822
+ {
823
+ Path = "/",
824
+ Projects = [
825
+ new ProjectDiscoveryResult()
826
+ {
827
+ FilePath = "project.csproj",
828
+ Dependencies = [new Dependency("Some.Dependency", "1.0.0", DependencyType.PackageReference)],
829
+ TargetFrameworks = ["net9.0"],
830
+ AdditionalFiles = [],
831
+ ImportedFiles = [],
832
+ ReferencedProjectPaths = []
833
+ }
834
+ ]
835
+ },
836
+ _ => throw new NotSupportedException($"Didn't expect {discoveryRequestCount} discovery requests"),
837
+ };
838
+ return Task.FromResult(result);
839
+ }),
840
+ dependencySolver: null, // use real worker
841
+ fileWriter: null, // use real worker
842
+ expectedProjectContents: """
843
+ <Project Sdk="Microsoft.NET.Sdk">
844
+ <PropertyGroup>
845
+ <TargetFramework>net9.0</TargetFramework>
846
+ </PropertyGroup>
847
+ <ItemGroup>
848
+ <PackageReference Include="Some.Dependency" Version="1.0.0" />
849
+ </ItemGroup>
850
+ </Project>
851
+ """,
852
+ expectedAdditionalFiles: [],
853
+ expectedOperations: []
854
+ );
855
+ }
856
+
857
+ private static async Task TestAsync(
858
+ string dependencyName,
859
+ string oldDependencyVersion,
860
+ string newDependencyVersion,
861
+ string projectContents,
862
+ (string name, string contents)[] additionalFiles,
863
+ IDiscoveryWorker? discoveryWorker,
864
+ IDependencySolver? dependencySolver,
865
+ IFileWriter? fileWriter,
866
+ string expectedProjectContents,
867
+ (string name, string contents)[] expectedAdditionalFiles,
868
+ UpdateOperationBase[] expectedOperations,
869
+ string projectName = "project.csproj",
870
+ MockNuGetPackage[]? packages = null,
871
+ ExperimentsManager? experimentsManager = null,
872
+ Action<DirectoryInfo>? additionalChecks = null
873
+ )
874
+ {
875
+ // arrange
876
+ var allFiles = new List<(string Path, string Contents)>() { (projectName, projectContents) };
877
+ allFiles.AddRange(additionalFiles);
878
+ using var tempDir = await TemporaryDirectory.CreateWithContentsAsync([.. allFiles]);
879
+ await UpdateWorkerTestBase.MockNuGetPackagesInDirectory(packages, tempDir.DirectoryPath);
880
+
881
+ var jobId = "TEST-JOB-ID";
882
+ var logger = new TestLogger();
883
+ experimentsManager ??= new ExperimentsManager() { UseDirectDiscovery = true };
884
+ discoveryWorker ??= new DiscoveryWorker(jobId, experimentsManager, logger);
885
+ var repoContentsPath = new DirectoryInfo(tempDir.DirectoryPath);
886
+ var projectPath = new FileInfo(Path.Combine(tempDir.DirectoryPath, projectName));
887
+ dependencySolver ??= new MSBuildDependencySolver(repoContentsPath, projectPath, experimentsManager, logger);
888
+ fileWriter ??= new XmlFileWriter(logger);
889
+
890
+ var fileWriterWorker = new FileWriterWorker(discoveryWorker, dependencySolver, fileWriter, logger);
891
+
892
+ // act
893
+ var actualUpdateOperations = await fileWriterWorker.RunAsync(
894
+ repoContentsPath,
895
+ projectPath,
896
+ dependencyName,
897
+ NuGetVersion.Parse(oldDependencyVersion),
898
+ NuGetVersion.Parse(newDependencyVersion)
899
+ );
900
+
901
+ // assert
902
+ var actualUpdateOperationsJson = actualUpdateOperations.Select(o => JsonSerializer.Serialize(o, RunWorker.SerializerOptions)).ToArray();
903
+ var expectedUpdateOperationsJson = expectedOperations.Select(o => JsonSerializer.Serialize(o, RunWorker.SerializerOptions)).ToArray();
904
+ AssertEx.Equal(expectedUpdateOperationsJson, actualUpdateOperationsJson);
905
+
906
+ var expectedFiles = new List<(string Path, string Contents)>() { (projectName, expectedProjectContents) };
907
+ expectedFiles.AddRange(expectedAdditionalFiles);
908
+ foreach (var (path, expectedContents) in expectedFiles)
909
+ {
910
+ var fullPath = Path.Join(tempDir.DirectoryPath, path);
911
+ var actualContents = await File.ReadAllTextAsync(fullPath);
912
+ Assert.Equal(expectedContents.Replace("\r", ""), actualContents.Replace("\r", ""));
913
+ }
914
+
915
+ additionalChecks?.Invoke(repoContentsPath);
916
+ }
917
+ }