dependabot-nuget 0.289.0 → 0.290.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/Directory.Packages.props +1 -1
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +7 -3
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +1 -1
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +26 -1
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +2 -1
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Run.cs +0 -6
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +3 -1
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +24 -9
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/NuGetContext.cs +0 -13
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementConverter.cs +17 -0
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Advisory.cs +13 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/AllowedUpdate.cs +18 -1
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/CommitOptions.cs +8 -0
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Condition.cs +19 -0
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyGroup.cs +8 -0
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/GroupPullRequest.cs +9 -0
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Job.cs +13 -10
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PullRequest.cs +11 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/RequirementsUpdateStrategy.cs +15 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +24 -4
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/VersionConverter.cs +19 -0
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +2 -1
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +13 -12
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/JsonHelper.cs +2 -0
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProjectHelper.cs +2 -2
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +5 -2
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.DotNetToolsJson.cs +45 -1
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.GlobalJson.cs +35 -1
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +0 -4
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MiscellaneousTests.cs +85 -0
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +7 -31
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +340 -0
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryDirectory.cs +18 -7
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs +24 -0
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +0 -12
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DotNetTools.cs +84 -0
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +66 -0
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +55 -0
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +0 -6
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +557 -713
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/PathHelperTests.cs +2 -2
  43. data/lib/dependabot/nuget/analysis/analysis_json_reader.rb +1 -1
  44. data/lib/dependabot/nuget/analysis/dependency_analysis.rb +3 -3
  45. data/lib/dependabot/nuget/discovery/dependency_details.rb +10 -3
  46. data/lib/dependabot/nuget/discovery/dependency_file_discovery.rb +8 -12
  47. data/lib/dependabot/nuget/discovery/discovery_json_reader.rb +214 -29
  48. data/lib/dependabot/nuget/discovery/project_discovery.rb +41 -8
  49. data/lib/dependabot/nuget/discovery/workspace_discovery.rb +14 -19
  50. data/lib/dependabot/nuget/file_fetcher.rb +2 -3
  51. data/lib/dependabot/nuget/file_parser.rb +2 -3
  52. data/lib/dependabot/nuget/file_updater.rb +13 -13
  53. data/lib/dependabot/nuget/native_helpers.rb +14 -5
  54. data/lib/dependabot/nuget/update_checker/requirements_updater.rb +23 -27
  55. data/lib/dependabot/nuget/update_checker.rb +116 -190
  56. metadata +18 -29
  57. data/lib/dependabot/nuget/discovery/directory_packages_props_discovery.rb +0 -43
  58. data/lib/dependabot/nuget/http_response_helpers.rb +0 -19
  59. data/lib/dependabot/nuget/native_discovery/native_dependency_details.rb +0 -102
  60. data/lib/dependabot/nuget/native_discovery/native_dependency_file_discovery.rb +0 -122
  61. data/lib/dependabot/nuget/native_discovery/native_discovery_json_reader.rb +0 -277
  62. data/lib/dependabot/nuget/native_discovery/native_evaluation_details.rb +0 -63
  63. data/lib/dependabot/nuget/native_discovery/native_project_discovery.rb +0 -104
  64. data/lib/dependabot/nuget/native_discovery/native_property_details.rb +0 -43
  65. data/lib/dependabot/nuget/native_discovery/native_workspace_discovery.rb +0 -61
  66. data/lib/dependabot/nuget/native_update_checker/native_requirements_updater.rb +0 -105
  67. data/lib/dependabot/nuget/native_update_checker/native_update_checker.rb +0 -214
  68. data/lib/dependabot/nuget/nuget_client.rb +0 -223
  69. data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +0 -116
  70. data/lib/dependabot/nuget/update_checker/dependency_finder.rb +0 -297
  71. data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +0 -221
  72. data/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb +0 -110
  73. data/lib/dependabot/nuget/update_checker/property_updater.rb +0 -196
  74. data/lib/dependabot/nuget/update_checker/repository_finder.rb +0 -466
  75. data/lib/dependabot/nuget/update_checker/tfm_comparer.rb +0 -34
  76. data/lib/dependabot/nuget/update_checker/tfm_finder.rb +0 -30
  77. data/lib/dependabot/nuget/update_checker/version_finder.rb +0 -449
@@ -19,7 +19,9 @@ public class AnalyzeWorkerTestBase
19
19
  DependencyInfo dependencyInfo,
20
20
  ExpectedAnalysisResult expectedResult,
21
21
  MockNuGetPackage[]? packages = null,
22
- TestFile[]? extraFiles = null)
22
+ TestFile[]? extraFiles = null,
23
+ ExperimentsManager? experimentsManager = null
24
+ )
23
25
  {
24
26
  var relativeDependencyPath = $"./dependabot/dependency/{dependencyInfo.Name}.json";
25
27
 
@@ -28,6 +30,7 @@ public class AnalyzeWorkerTestBase
28
30
  (relativeDependencyPath, JsonSerializer.Serialize(dependencyInfo, AnalyzeWorker.SerializerOptions)),
29
31
  ];
30
32
 
33
+ experimentsManager ??= new ExperimentsManager();
31
34
  var allFiles = files.Concat(extraFiles ?? []).ToArray();
32
35
  var actualResult = await RunAnalyzerAsync(dependencyInfo.Name, allFiles, async directoryPath =>
33
36
  {
@@ -36,7 +39,7 @@ public class AnalyzeWorkerTestBase
36
39
  var discoveryPath = Path.GetFullPath(DiscoveryWorker.DiscoveryResultFileName, directoryPath);
37
40
  var dependencyPath = Path.GetFullPath(relativeDependencyPath, directoryPath);
38
41
 
39
- var worker = new AnalyzeWorker(new TestLogger());
42
+ var worker = new AnalyzeWorker(experimentsManager, new TestLogger());
40
43
  var result = await worker.RunWithErrorHandlingAsync(directoryPath, discoveryPath, dependencyPath);
41
44
  return result;
42
45
  });
@@ -50,6 +50,50 @@ public partial class DiscoveryWorkerTests
50
50
  );
51
51
  }
52
52
 
53
+ [Fact]
54
+ public async Task DiscoversDependenciesTrailingComma()
55
+ {
56
+ await TestDiscoveryAsync(
57
+ packages: [],
58
+ workspacePath: "",
59
+ files: [
60
+ (".config/dotnet-tools.json", """
61
+ {
62
+ "version": 1,
63
+ "isRoot": true,
64
+ "tools": {
65
+ "botsay": {
66
+ "version": "1.0.0",
67
+ "commands": [
68
+ "botsay"
69
+ ],
70
+ },
71
+ "dotnetsay": {
72
+ "version": "1.0.0",
73
+ "commands": [
74
+ "dotnetsay"
75
+ ],
76
+ }
77
+ }
78
+ }
79
+ """),
80
+ ],
81
+ expectedResult: new()
82
+ {
83
+ Path = "",
84
+ DotNetToolsJson = new()
85
+ {
86
+ FilePath = ".config/dotnet-tools.json",
87
+ Dependencies = [
88
+ new("botsay", "1.0.0", DependencyType.DotNetTool),
89
+ new("dotnetsay", "1.0.0", DependencyType.DotNetTool),
90
+ ]
91
+ },
92
+ ExpectedProjectCount = 0,
93
+ }
94
+ );
95
+ }
96
+
53
97
  [Fact]
54
98
  public async Task ReportsFailure()
55
99
  {
@@ -74,7 +118,7 @@ public partial class DiscoveryWorkerTests
74
118
  "dotnetsay"
75
119
  ]
76
120
  }
77
- }
121
+ } INVALID JSON
78
122
  }
79
123
  """),
80
124
  ],
@@ -40,6 +40,40 @@ public partial class DiscoveryWorkerTests
40
40
  );
41
41
  }
42
42
 
43
+ [Fact]
44
+ public async Task DiscoversDependencies_HandlesTrailingComma()
45
+ {
46
+ await TestDiscoveryAsync(
47
+ packages: [],
48
+ workspacePath: "",
49
+ files: [
50
+ ("global.json", """
51
+ {
52
+ "sdk": {
53
+ "version": "2.2.104"
54
+ },
55
+ "msbuild-sdks": {
56
+ "Microsoft.Build.Traversal": "1.0.45"
57
+ },
58
+ }
59
+ """),
60
+ ],
61
+ expectedResult: new()
62
+ {
63
+ Path = "",
64
+ GlobalJson = new()
65
+ {
66
+ FilePath = "global.json",
67
+ Dependencies = [
68
+ new("Microsoft.NET.Sdk", "2.2.104", DependencyType.MSBuildSdk),
69
+ new("Microsoft.Build.Traversal", "1.0.45", DependencyType.MSBuildSdk),
70
+ ]
71
+ },
72
+ ExpectedProjectCount = 0,
73
+ }
74
+ );
75
+ }
76
+
43
77
  [Fact]
44
78
  public async Task ReportsFailure()
45
79
  {
@@ -50,7 +84,7 @@ public partial class DiscoveryWorkerTests
50
84
  ("global.json", """
51
85
  {
52
86
  "sdk": {
53
- "version": "2.2.104",
87
+ "version": "2.2.104", INVALID JSON
54
88
  },
55
89
  "msbuild-sdks": {
56
90
  "Microsoft.Build.Traversal": "1.0.45"
@@ -441,8 +441,6 @@ public partial class DiscoveryWorkerTests
441
441
  ReferencedProjectPaths = [],
442
442
  ImportedFiles = [
443
443
  "Directory.Build.targets",
444
- "NUGET_PACKAGES/microsoft.build.centralpackageversions/2.1.3/Sdk/Sdk.props", // this is an artifact of the package cache existing next to the csproj
445
- "NUGET_PACKAGES/microsoft.build.centralpackageversions/2.1.3/Sdk/Sdk.targets", // this is an artifact of the package cache existing next to the csproj
446
444
  "Packages.props",
447
445
  ],
448
446
  AdditionalFiles = [],
@@ -513,8 +511,6 @@ public partial class DiscoveryWorkerTests
513
511
  ReferencedProjectPaths = [],
514
512
  ImportedFiles = [
515
513
  "Directory.Build.targets",
516
- "NUGET_PACKAGES/microsoft.build.centralpackageversions/2.1.3/Sdk/Sdk.props", // this is an artifact of the package cache existing next to the csproj
517
- "NUGET_PACKAGES/microsoft.build.centralpackageversions/2.1.3/Sdk/Sdk.targets", // this is an artifact of the package cache existing next to the csproj
518
514
  "Packages.props",
519
515
  ],
520
516
  AdditionalFiles = [],
@@ -0,0 +1,85 @@
1
+ using NuGet.Versioning;
2
+
3
+ using NuGetUpdater.Core.Analyze;
4
+ using NuGetUpdater.Core.Run;
5
+ using NuGetUpdater.Core.Run.ApiModel;
6
+
7
+ using Xunit;
8
+
9
+ namespace NuGetUpdater.Core.Test.Run;
10
+
11
+ public class MiscellaneousTests
12
+ {
13
+ [Theory]
14
+ [MemberData(nameof(RequirementsFromIgnoredVersionsData))]
15
+ public void RequirementsFromIgnoredVersions(string dependencyName, Condition[] ignoreConditions, Requirement[] expectedRequirements)
16
+ {
17
+ var job = new Job()
18
+ {
19
+ Source = new()
20
+ {
21
+ Provider = "github",
22
+ Repo = "some/repo"
23
+ },
24
+ IgnoreConditions = ignoreConditions
25
+ };
26
+ var actualRequirements = RunWorker.GetIgnoredRequirementsForDependency(job, dependencyName);
27
+ var actualRequirementsStrings = string.Join("|", actualRequirements.Select(r => r.ToString()));
28
+ var expectedRequirementsStrings = string.Join("|", expectedRequirements.Select(r => r.ToString()));
29
+ Assert.Equal(expectedRequirementsStrings, actualRequirementsStrings);
30
+ }
31
+
32
+ public static IEnumerable<object?[]> RequirementsFromIgnoredVersionsData()
33
+ {
34
+ yield return
35
+ [
36
+ // dependencyName
37
+ "Some.Package",
38
+ // ignoredConditions
39
+ new Condition[]
40
+ {
41
+ new()
42
+ {
43
+ DependencyName = "SOME.PACKAGE",
44
+ VersionRequirement = Requirement.Parse("> 1.2.3")
45
+ },
46
+ new()
47
+ {
48
+ DependencyName = "some.package",
49
+ VersionRequirement = Requirement.Parse("<= 2.0.0")
50
+ },
51
+ new()
52
+ {
53
+ DependencyName = "Unrelated.Package",
54
+ VersionRequirement = Requirement.Parse("= 3.4.5")
55
+ }
56
+ },
57
+ // expectedRequirements
58
+ new Requirement[]
59
+ {
60
+ new IndividualRequirement(">", NuGetVersion.Parse("1.2.3")),
61
+ new IndividualRequirement("<=", NuGetVersion.Parse("2.0.0")),
62
+ }
63
+ ];
64
+
65
+ // version requirement is null => ignore all
66
+ yield return
67
+ [
68
+ // dependencyName
69
+ "Some.Package",
70
+ // ignoredConditions
71
+ new Condition[]
72
+ {
73
+ new()
74
+ {
75
+ DependencyName = "Some.Package"
76
+ }
77
+ },
78
+ // expectedRequirements
79
+ new Requirement[]
80
+ {
81
+ new IndividualRequirement(">", NuGetVersion.Parse("0.0.0"))
82
+ }
83
+ ];
84
+ }
85
+ }
@@ -30,11 +30,7 @@ public class RunWorkerTests
30
30
  Provider = "github",
31
31
  Repo = "test/repo",
32
32
  Directory = "some-dir",
33
- },
34
- AllowedUpdates =
35
- [
36
- new() { UpdateType = "all" }
37
- ]
33
+ }
38
34
  },
39
35
  files:
40
36
  [
@@ -237,11 +233,7 @@ public class RunWorkerTests
237
233
  Provider = "github",
238
234
  Repo = "test/repo",
239
235
  Directory = "some-dir",
240
- },
241
- AllowedUpdates =
242
- [
243
- new() { UpdateType = "all" }
244
- ]
236
+ }
245
237
  },
246
238
  files:
247
239
  [
@@ -483,11 +475,7 @@ public class RunWorkerTests
483
475
  Provider = "github",
484
476
  Repo = "test/repo",
485
477
  Directory = "/",
486
- },
487
- AllowedUpdates =
488
- [
489
- new() { UpdateType = "all" }
490
- ]
478
+ }
491
479
  },
492
480
  files:
493
481
  [
@@ -550,11 +538,7 @@ public class RunWorkerTests
550
538
  Provider = "github",
551
539
  Repo = "test/repo",
552
540
  Directory = "some-dir",
553
- },
554
- AllowedUpdates =
555
- [
556
- new() { UpdateType = "all" }
557
- ]
541
+ }
558
542
  },
559
543
  files:
560
544
  [
@@ -886,11 +870,7 @@ public class RunWorkerTests
886
870
  Provider = "github",
887
871
  Repo = "test/repo",
888
872
  Directory = "some-dir/ProjectA",
889
- },
890
- AllowedUpdates =
891
- [
892
- new() { UpdateType = "all" }
893
- ]
873
+ }
894
874
  },
895
875
  files:
896
876
  [
@@ -1438,11 +1418,7 @@ public class RunWorkerTests
1438
1418
  Provider = "github",
1439
1419
  Repo = "test/repo",
1440
1420
  Directory = "/",
1441
- },
1442
- AllowedUpdates =
1443
- [
1444
- new() { UpdateType = "all" }
1445
- ]
1421
+ }
1446
1422
  },
1447
1423
  packages:
1448
1424
  [
@@ -1754,7 +1730,7 @@ public class RunWorkerTests
1754
1730
  var testApiHandler = new TestApiHandler();
1755
1731
  var logger = new TestLogger();
1756
1732
  discoveryWorker ??= new DiscoveryWorker(experimentsManager, logger);
1757
- analyzeWorker ??= new AnalyzeWorker(logger);
1733
+ analyzeWorker ??= new AnalyzeWorker(experimentsManager, logger);
1758
1734
  updaterWorker ??= new UpdaterWorker(experimentsManager, logger);
1759
1735
 
1760
1736
  var worker = new RunWorker(testApiHandler, discoveryWorker, analyzeWorker, updaterWorker, logger);
@@ -1,5 +1,9 @@
1
+ using NuGet.Versioning;
2
+
3
+ using NuGetUpdater.Core.Analyze;
1
4
  using NuGetUpdater.Core.Run;
2
5
  using NuGetUpdater.Core.Run.ApiModel;
6
+ using NuGetUpdater.Core.Test.Utilities;
3
7
 
4
8
  using Xunit;
5
9
 
@@ -257,6 +261,342 @@ public class SerializationTests
257
261
  Assert.Equal(expected, actual);
258
262
  }
259
263
 
264
+ [Fact]
265
+ public void DeserializeJobIgnoreConditions()
266
+ {
267
+ var jobContent = """
268
+ {
269
+ "job": {
270
+ "package-manager": "nuget",
271
+ "source": {
272
+ "provider": "github",
273
+ "repo": "some-org/some-repo",
274
+ "directory": "specific-sdk"
275
+ },
276
+ "ignore-conditions": [
277
+ {
278
+ "dependency-name": "Package.1",
279
+ "source": "some-file",
280
+ "update-types": [
281
+ "version-update:semver-major"
282
+ ],
283
+ "version-requirement": "> 1.2.3"
284
+ },
285
+ {
286
+ "dependency-name": "Package.2",
287
+ "updated-at": "2024-12-05T15:47:12Z"
288
+ }
289
+ ]
290
+ }
291
+ }
292
+ """;
293
+ var jobWrapper = RunWorker.Deserialize(jobContent)!;
294
+ Assert.Equal(2, jobWrapper.Job.IgnoreConditions.Length);
295
+
296
+ Assert.Equal("Package.1", jobWrapper.Job.IgnoreConditions[0].DependencyName);
297
+ Assert.Equal("some-file", jobWrapper.Job.IgnoreConditions[0].Source);
298
+ Assert.Equal("version-update:semver-major", jobWrapper.Job.IgnoreConditions[0].UpdateTypes.Single());
299
+ Assert.Null(jobWrapper.Job.IgnoreConditions[0].UpdatedAt);
300
+ Assert.Equal("> 1.2.3", jobWrapper.Job.IgnoreConditions[0].VersionRequirement?.ToString());
301
+
302
+ Assert.Equal("Package.2", jobWrapper.Job.IgnoreConditions[1].DependencyName);
303
+ Assert.Null(jobWrapper.Job.IgnoreConditions[1].Source);
304
+ Assert.Empty(jobWrapper.Job.IgnoreConditions[1].UpdateTypes);
305
+ Assert.Equal(new DateTime(2024, 12, 5, 15, 47, 12), jobWrapper.Job.IgnoreConditions[1].UpdatedAt);
306
+ Assert.Null(jobWrapper.Job.IgnoreConditions[1].VersionRequirement);
307
+ }
308
+
309
+ [Theory]
310
+ [MemberData(nameof(DeserializeAllowedUpdatesData))]
311
+ public void DeserializeAllowedUpdates(string? allowedUpdatesJsonBody, AllowedUpdate[] expectedAllowedUpdates)
312
+ {
313
+ string? allowedUpdatesJson = allowedUpdatesJsonBody is null
314
+ ? null
315
+ : $$"""
316
+ ,
317
+ "allowed-updates": {{allowedUpdatesJsonBody}}
318
+ """;
319
+ var jobWrapperJson = $$"""
320
+ {
321
+ "job": {
322
+ "source": {
323
+ "provider": "github",
324
+ "repo": "some/repo"
325
+ }
326
+ {{allowedUpdatesJson}}
327
+ }
328
+ }
329
+ """;
330
+ var jobWrapper = RunWorker.Deserialize(jobWrapperJson)!;
331
+ AssertEx.Equal(expectedAllowedUpdates, jobWrapper.Job.AllowedUpdates);
332
+ }
333
+
334
+ [Fact]
335
+ public void DeserializeDependencyGroups()
336
+ {
337
+ var jsonWrapperJson = """
338
+ {
339
+ "job": {
340
+ "source": {
341
+ "provider": "github",
342
+ "repo": "some/repo"
343
+ },
344
+ "dependency-groups": [
345
+ {
346
+ "name": "Some.Dependency",
347
+ "rules": {
348
+ "patterns": ["1.2.3", "4.5.6"]
349
+ }
350
+ },
351
+ {
352
+ "name": "Some.Other.Dependency",
353
+ "applies-to": "something"
354
+ }
355
+ ]
356
+ }
357
+ }
358
+ """;
359
+ var jobWrapper = RunWorker.Deserialize(jsonWrapperJson)!;
360
+ Assert.Equal(2, jobWrapper.Job.DependencyGroups.Length);
361
+
362
+ Assert.Equal("Some.Dependency", jobWrapper.Job.DependencyGroups[0].Name);
363
+ Assert.Null(jobWrapper.Job.DependencyGroups[0].AppliesTo);
364
+ Assert.Single(jobWrapper.Job.DependencyGroups[0].Rules);
365
+ Assert.Equal("[\"1.2.3\", \"4.5.6\"]", jobWrapper.Job.DependencyGroups[0].Rules["patterns"].ToString());
366
+
367
+ Assert.Equal("Some.Other.Dependency", jobWrapper.Job.DependencyGroups[1].Name);
368
+ Assert.Equal("something", jobWrapper.Job.DependencyGroups[1].AppliesTo);
369
+ Assert.Empty(jobWrapper.Job.DependencyGroups[1].Rules);
370
+ }
371
+
372
+ [Fact]
373
+ public void DeserializeExistingPullRequests()
374
+ {
375
+ var jsonWrapperJson = """
376
+ {
377
+ "job": {
378
+ "source": {
379
+ "provider": "github",
380
+ "repo": "some/repo"
381
+ },
382
+ "existing-pull-requests": [
383
+ [
384
+ {
385
+ "dependency-name": "Some.Package",
386
+ "dependency-version": "1.2.3"
387
+ }
388
+ ]
389
+ ]
390
+ }
391
+ }
392
+ """;
393
+ var jobWrapper = RunWorker.Deserialize(jsonWrapperJson)!;
394
+ Assert.Single(jobWrapper.Job.ExistingPullRequests);
395
+ Assert.Single(jobWrapper.Job.ExistingPullRequests[0]);
396
+ Assert.Equal("Some.Package", jobWrapper.Job.ExistingPullRequests[0][0].DependencyName);
397
+ Assert.Equal(NuGetVersion.Parse("1.2.3"), jobWrapper.Job.ExistingPullRequests[0][0].DependencyVersion);
398
+ Assert.False(jobWrapper.Job.ExistingPullRequests[0][0].DependencyRemoved);
399
+ Assert.Null(jobWrapper.Job.ExistingPullRequests[0][0].Directory);
400
+ }
401
+
402
+ [Fact]
403
+ public void DeserializeExistingGroupPullRequests()
404
+ {
405
+ var jsonWrapperJson = """
406
+ {
407
+ "job": {
408
+ "source": {
409
+ "provider": "github",
410
+ "repo": "some/repo"
411
+ },
412
+ "existing-group-pull-requests": [
413
+ {
414
+ "dependency-group-name": "Some-Group-Name",
415
+ "dependencies": [
416
+ {
417
+ "dependency-name": "Some.Package",
418
+ "dependency-version": "1.2.3"
419
+ },
420
+ {
421
+ "dependency-name": "Some.Other.Package",
422
+ "dependency-version": "4.5.6",
423
+ "directory": "/some-dir"
424
+ }
425
+ ]
426
+ }
427
+ ]
428
+ }
429
+ }
430
+ """;
431
+ var jobWrapper = RunWorker.Deserialize(jsonWrapperJson)!;
432
+ Assert.Single(jobWrapper.Job.ExistingGroupPullRequests);
433
+ Assert.Equal("Some-Group-Name", jobWrapper.Job.ExistingGroupPullRequests[0].DependencyGroupName);
434
+ Assert.Equal(2, jobWrapper.Job.ExistingGroupPullRequests[0].Dependencies.Length);
435
+ Assert.Equal("Some.Package", jobWrapper.Job.ExistingGroupPullRequests[0].Dependencies[0].DependencyName);
436
+ Assert.Equal("1.2.3", jobWrapper.Job.ExistingGroupPullRequests[0].Dependencies[0].DependencyVersion.ToString());
437
+ Assert.Null(jobWrapper.Job.ExistingGroupPullRequests[0].Dependencies[0].Directory);
438
+ Assert.Equal("Some.Other.Package", jobWrapper.Job.ExistingGroupPullRequests[0].Dependencies[1].DependencyName);
439
+ Assert.Equal("4.5.6", jobWrapper.Job.ExistingGroupPullRequests[0].Dependencies[1].DependencyVersion.ToString());
440
+ Assert.Equal("/some-dir", jobWrapper.Job.ExistingGroupPullRequests[0].Dependencies[1].Directory);
441
+ }
442
+
443
+ [Theory]
444
+ [InlineData("null", null)]
445
+ [InlineData("\"bump_versions\"", RequirementsUpdateStrategy.BumpVersions)]
446
+ [InlineData("\"lockfile_only\"", RequirementsUpdateStrategy.LockfileOnly)]
447
+ public void DeserializeRequirementsUpdateStrategy(string requirementsUpdateStrategyStringJson, RequirementsUpdateStrategy? expectedRequirementsUpdateStrategy)
448
+ {
449
+ var jsonWrapperJson = $$"""
450
+ {
451
+ "job": {
452
+ "source": {
453
+ "provider": "github",
454
+ "repo": "some/repo"
455
+ },
456
+ "requirements-update-strategy": {{requirementsUpdateStrategyStringJson}}
457
+ }
458
+ }
459
+ """;
460
+ var jobWrapper = RunWorker.Deserialize(jsonWrapperJson)!;
461
+ var actualRequirementsUpdateStrategy = jobWrapper.Job.RequirementsUpdateStrategy;
462
+ Assert.Equal(expectedRequirementsUpdateStrategy, actualRequirementsUpdateStrategy);
463
+ }
464
+
465
+ [Fact]
466
+ public void DeserializeSecurityAdvisories()
467
+ {
468
+ var jsonWrapperJson = """
469
+ {
470
+ "job": {
471
+ "source": {
472
+ "provider": "github",
473
+ "repo": "some/repo"
474
+ },
475
+ "security-advisories": [
476
+ {
477
+ "dependency-name": "Some.Package",
478
+ "affected-versions": [
479
+ ">= 1.0.0, < 1.2.0"
480
+ ],
481
+ "patched-versions": null
482
+ }
483
+ ]
484
+ }
485
+ }
486
+ """;
487
+ var jobWrapper = RunWorker.Deserialize(jsonWrapperJson)!;
488
+ Assert.Single(jobWrapper.Job.SecurityAdvisories);
489
+ Assert.Equal("Some.Package", jobWrapper.Job.SecurityAdvisories[0].DependencyName);
490
+ Assert.Equal(">= 1.0.0, < 1.2.0", jobWrapper.Job.SecurityAdvisories[0].AffectedVersions!.Value.Single().ToString());
491
+ Assert.Null(jobWrapper.Job.SecurityAdvisories[0].PatchedVersions);
492
+ Assert.Null(jobWrapper.Job.SecurityAdvisories[0].PatchedVersions);
493
+ }
494
+
495
+ [Fact]
496
+ public void DeserializeCommitOptions()
497
+ {
498
+ var jsonWrapperJson = """
499
+ {
500
+ "job": {
501
+ "source": {
502
+ "provider": "github",
503
+ "repo": "some/repo"
504
+ },
505
+ "commit-message-options": {
506
+ "prefix": "[SECURITY] "
507
+ }
508
+ }
509
+ }
510
+ """;
511
+ var jobWrapper = RunWorker.Deserialize(jsonWrapperJson)!;
512
+ Assert.Equal("[SECURITY] ", jobWrapper.Job.CommitMessageOptions!.Prefix);
513
+ Assert.Null(jobWrapper.Job.CommitMessageOptions!.PrefixDevelopment);
514
+ Assert.Null(jobWrapper.Job.CommitMessageOptions!.IncludeScope);
515
+ }
516
+
517
+ public static IEnumerable<object?[]> DeserializeAllowedUpdatesData()
518
+ {
519
+ // common default value - most job files look like this
520
+ yield return
521
+ [
522
+ // allowedUpdatesJsonBody
523
+ """
524
+ [
525
+ {
526
+ "update-type": "all"
527
+ }
528
+ ]
529
+ """,
530
+ // expectedAllowedUpdates
531
+ new[]
532
+ {
533
+ new AllowedUpdate()
534
+ {
535
+ DependencyType = Core.Run.ApiModel.DependencyType.All,
536
+ DependencyName = null,
537
+ UpdateType = UpdateType.All
538
+ }
539
+ }
540
+ ];
541
+
542
+ // allowed updates is missing - ensure proper defaults
543
+ yield return
544
+ [
545
+ // allowedUpdatesJsonBody
546
+ null,
547
+ // expectedAllowedUpdates
548
+ new[]
549
+ {
550
+ new AllowedUpdate()
551
+ }
552
+ ];
553
+
554
+ // multiple non-default values
555
+ yield return
556
+ [
557
+ // allowedUpdatesJsonBody
558
+ """
559
+ [
560
+ {
561
+ "dependency-type": "indirect",
562
+ "dependency-name": "Dependency.One",
563
+ "update-type": "security"
564
+ },
565
+ {
566
+ "dependency-type": "production",
567
+ "dependency-name": "Dependency.Two",
568
+ "update-type": "all"
569
+ },
570
+ {
571
+ "dependency-type": "indirect",
572
+ "update-type": "security"
573
+ }
574
+ ]
575
+ """,
576
+ new[]
577
+ {
578
+ new AllowedUpdate()
579
+ {
580
+ DependencyType = Core.Run.ApiModel.DependencyType.Indirect,
581
+ DependencyName = "Dependency.One",
582
+ UpdateType = UpdateType.Security
583
+ },
584
+ new AllowedUpdate()
585
+ {
586
+ DependencyType = Core.Run.ApiModel.DependencyType.Production,
587
+ DependencyName = "Dependency.Two",
588
+ UpdateType = UpdateType.All
589
+ },
590
+ new AllowedUpdate()
591
+ {
592
+ DependencyType = Core.Run.ApiModel.DependencyType.Indirect,
593
+ DependencyName = null,
594
+ UpdateType = UpdateType.Security
595
+ }
596
+ }
597
+ ];
598
+ }
599
+
260
600
  private class CapturingTestLogger : ILogger
261
601
  {
262
602
  private readonly List<string> _messages = new();