dependabot-nuget 0.291.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 (51) 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.Core/Analyze/AnalyzeWorker.cs +11 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementConverter.cs +19 -1
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/BadRequirementException.cs +9 -0
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Clone/CloneWorker.cs +39 -8
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +67 -12
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ErrorType.cs +1 -0
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +28 -5
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +1 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/BadRequirement.cs +10 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/CommitOptions.cs +1 -1
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyFileNotFound.cs +3 -2
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +1 -2
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobRepoNotFound.cs +1 -4
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PrivateSourceAuthenticationFailure.cs +1 -1
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UnknownError.cs +6 -2
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UpdateNotPossible.cs +1 -1
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +9 -3
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs +1 -1
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/WebApplicationTargetsConditionPatcher.cs +12 -1
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DependencyConflictResolver.cs +0 -7
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +8 -3
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProjectHelper.cs +4 -4
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Clone/CloneWorkerTests.cs +60 -2
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +10 -1
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +56 -0
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +1 -0
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +1 -1
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +76 -40
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +20 -2
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +2 -2
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.LockFile.cs +251 -0
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +6 -6
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +63 -5
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +38 -20
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/ProjectHelperTests.cs +65 -0
  47. data/helpers/lib/NuGetUpdater/global.json +1 -1
  48. data/lib/dependabot/nuget/language.rb +21 -5
  49. data/lib/dependabot/nuget/native_helpers.rb +2 -0
  50. data/lib/dependabot/nuget/package_manager.rb +4 -4
  51. metadata +10 -6
@@ -12,6 +12,7 @@ namespace NuGetUpdater.Core.Run;
12
12
 
13
13
  public class RunWorker
14
14
  {
15
+ private readonly string _jobId;
15
16
  private readonly IApiHandler _apiHandler;
16
17
  private readonly ILogger _logger;
17
18
  private readonly IDiscoveryWorker _discoveryWorker;
@@ -25,8 +26,9 @@ public class RunWorker
25
26
  Converters = { new JsonStringEnumConverter(), new RequirementConverter(), new VersionConverter() },
26
27
  };
27
28
 
28
- public RunWorker(IApiHandler apiHandler, IDiscoveryWorker discoverWorker, IAnalyzeWorker analyzeWorker, IUpdaterWorker updateWorker, ILogger logger)
29
+ public RunWorker(string jobId, IApiHandler apiHandler, IDiscoveryWorker discoverWorker, IAnalyzeWorker analyzeWorker, IUpdaterWorker updateWorker, ILogger logger)
29
30
  {
31
+ _jobId = jobId;
30
32
  _apiHandler = apiHandler;
31
33
  _logger = logger;
32
34
  _discoveryWorker = discoverWorker;
@@ -87,9 +89,13 @@ public class RunWorker
87
89
  {
88
90
  error = new PrivateSourceAuthenticationFailure(lastUsedPackageSourceUrls);
89
91
  }
92
+ catch (BadRequirementException ex)
93
+ {
94
+ error = new BadRequirement(ex.Message);
95
+ }
90
96
  catch (MissingFileException ex)
91
97
  {
92
- error = new DependencyFileNotFound(ex.FilePath);
98
+ error = new DependencyFileNotFound("file not found", ex.FilePath);
93
99
  }
94
100
  catch (UpdateNotPossibleException ex)
95
101
  {
@@ -97,7 +103,7 @@ public class RunWorker
97
103
  }
98
104
  catch (Exception ex)
99
105
  {
100
- error = new UnknownError(ex.ToString());
106
+ error = new UnknownError(ex, _jobId);
101
107
  }
102
108
 
103
109
  if (error is not null)
@@ -11,7 +11,7 @@ internal static class LockFileUpdater
11
11
  var projectDirectory = Path.GetDirectoryName(projectPath)!;
12
12
  await MSBuildHelper.HandleGlobalJsonAsync(projectDirectory, repoRootPath, experimentsManager, async () =>
13
13
  {
14
- var (exitCode, stdout, stderr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["restore", "--force-evaluate", projectPath], projectDirectory, experimentsManager);
14
+ var (exitCode, stdout, stderr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["restore", "--force-evaluate", "-p:EnableWindowsTargeting=true", projectPath], projectDirectory, experimentsManager);
15
15
  if (exitCode != 0)
16
16
  {
17
17
  logger.Error($" Lock file update failed.\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}");
@@ -13,7 +13,18 @@ namespace NuGetUpdater.Core.Updater
13
13
  getContent: () => File.ReadAllText(projectFilePath),
14
14
  setContent: s => File.WriteAllText(projectFilePath, s),
15
15
  nodeFinder: doc => doc.Descendants()
16
- .FirstOrDefault(e => e.Name == "Import" && e.GetAttributeValue("Project") == @"$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets")
16
+ .Where(e => e.Name == "Import")
17
+ .FirstOrDefault(e =>
18
+ {
19
+ var projectPath = e.GetAttributeValue("Project");
20
+ if (projectPath is not null)
21
+ {
22
+ var projectFileName = Path.GetFileName(projectPath.NormalizePathToUnix());
23
+ return projectFileName.Equals("Microsoft.WebApplication.targets", StringComparison.OrdinalIgnoreCase);
24
+ }
25
+
26
+ return false;
27
+ })
17
28
  as XmlNodeSyntax,
18
29
  preProcessor: n =>
19
30
  {
@@ -395,7 +395,6 @@ public class PackageManager
395
395
  if (await AreAllParentsCompatibleAsync(existingPackages, existingPackage, targetFramework, projectDirectory, logger) == true)
396
396
  {
397
397
  existingPackage.CurrentVersion = dependencyOldVersion;
398
- string NewVersion = dependency.CurrentVersion;
399
398
  existingPackage.NewVersion = dependency.CurrentVersion;
400
399
  await UpdateVersion(existingPackages, existingPackage, targetFramework, projectDirectory, logger);
401
400
  }
@@ -593,12 +592,6 @@ public class PackageManager
593
592
  return null;
594
593
  }
595
594
 
596
- // If the current version of the parent is less than the current version of the dependency
597
- else if (CurrentVersion < currentVersionDependency)
598
- {
599
- return currentVersionDependency;
600
- }
601
-
602
595
  // Loop from the current version to the latest version, use next patch as a limit (unless there's a limit) so it doesn't look for versions that don't exist
603
596
  for (NuGetVersion version = CurrentVersion; version <= latestVersion; version = NextPatch(version, versions))
604
597
  {
@@ -886,6 +886,7 @@ internal static partial class MSBuildHelper
886
886
  "The plugin credential provider could not acquire credentials",
887
887
  "401 (Unauthorized)",
888
888
  "error NU1301: Unable to load the service index for source",
889
+ "Response status code does not indicate success: 403",
889
890
  };
890
891
  if (unauthorizedMessageSnippets.Any(stdout.Contains))
891
892
  {
@@ -895,9 +896,13 @@ internal static partial class MSBuildHelper
895
896
 
896
897
  internal static string? GetMissingFile(string output)
897
898
  {
898
- var missingFilePattern = new Regex(@"The imported project \""(?<FilePath>.*)\"" was not found");
899
- var match = missingFilePattern.Match(output);
900
- if (match.Success)
899
+ var missingFilePatterns = new[]
900
+ {
901
+ new Regex(@"The imported project \""(?<FilePath>.*)\"" was not found"),
902
+ new Regex(@"The imported file \""(?<FilePath>.*)\"" does not exist"),
903
+ };
904
+ var match = missingFilePatterns.Select(p => p.Match(output)).Where(m => m.Success).FirstOrDefault();
905
+ if (match is not null)
901
906
  {
902
907
  return match.Groups["FilePath"].Value;
903
908
  }
@@ -77,11 +77,11 @@ internal static class ProjectHelper
77
77
  var itemPath = projectRootElement.Items
78
78
  .Where(i => i.ElementName.Equals("None", StringComparison.OrdinalIgnoreCase) ||
79
79
  i.ElementName.Equals("Content", StringComparison.OrdinalIgnoreCase))
80
- .Where(i => Path.GetFileName(i.Include).Equals(itemFileName, StringComparison.OrdinalIgnoreCase))
81
- .Select(i => Path.GetFullPath(Path.Combine(projectDirectory, i.Include)))
80
+ .Where(i => !string.IsNullOrEmpty(i.Include))
81
+ .Select(i => Path.GetFullPath(Path.Combine(projectDirectory, i.Include.NormalizePathToUnix())))
82
+ .Where(p => Path.GetFileName(p).Equals(itemFileName, StringComparison.OrdinalIgnoreCase))
82
83
  .Where(File.Exists)
83
- .FirstOrDefault()
84
- ?.NormalizePathToUnix();
84
+ .FirstOrDefault();
85
85
  return itemPath;
86
86
  }
87
87
 
@@ -95,6 +95,65 @@ public class CloneWorkerTests
95
95
  );
96
96
  }
97
97
 
98
+ [Fact]
99
+ public async Task JobFileParseErrorIsReported_InvalidJson()
100
+ {
101
+ // arrange
102
+ var testApiHandler = new TestApiHandler();
103
+ var testGitCommandHandler = new TestGitCommandHandler();
104
+ var cloneWorker = new CloneWorker("JOB-ID", testApiHandler, testGitCommandHandler);
105
+ using var testDirectory = new TemporaryDirectory();
106
+ var jobFilePath = Path.Combine(testDirectory.DirectoryPath, "job.json");
107
+ await File.WriteAllTextAsync(jobFilePath, "not json");
108
+
109
+ // act
110
+ var result = await cloneWorker.RunAsync(new FileInfo(jobFilePath), new DirectoryInfo(testDirectory.DirectoryPath));
111
+
112
+ // assert
113
+ Assert.Equal(1, result);
114
+ var expectedParseErrorObject = testApiHandler.ReceivedMessages.Single(m => m.Type == typeof(UnknownError));
115
+ var unknownError = (UnknownError)expectedParseErrorObject.Object;
116
+ Assert.Equal("JsonException", unknownError.Details["error-class"]);
117
+ }
118
+
119
+ [Fact]
120
+ public async Task JobFileParseErrorIsReported_BadRequirement()
121
+ {
122
+ // arrange
123
+ var testApiHandler = new TestApiHandler();
124
+ var testGitCommandHandler = new TestGitCommandHandler();
125
+ var cloneWorker = new CloneWorker("JOB-ID", testApiHandler, testGitCommandHandler);
126
+ using var testDirectory = new TemporaryDirectory();
127
+ var jobFilePath = Path.Combine(testDirectory.DirectoryPath, "job.json");
128
+
129
+ // write a job file with a valid shape, but invalid requirement
130
+ await File.WriteAllTextAsync(jobFilePath, """
131
+ {
132
+ "job": {
133
+ "source": {
134
+ "provider": "github",
135
+ "repo": "test/repo"
136
+ },
137
+ "security-advisories": [
138
+ {
139
+ "dependency-name": "Some.Dependency",
140
+ "affected-versions": ["not a valid requirement"]
141
+ }
142
+ ]
143
+ }
144
+ }
145
+ """);
146
+
147
+ // act
148
+ var result = await cloneWorker.RunAsync(new FileInfo(jobFilePath), new DirectoryInfo(testDirectory.DirectoryPath));
149
+
150
+ // assert
151
+ Assert.Equal(1, result);
152
+ var expectedParseErrorObject = testApiHandler.ReceivedMessages.Single(m => m.Type == typeof(BadRequirement));
153
+ var badRequirement = (BadRequirement)expectedParseErrorObject.Object;
154
+ Assert.Equal("not a valid requirement", badRequirement.Details["message"]);
155
+ }
156
+
98
157
  private class TestGitCommandHandlerWithOutputs : TestGitCommandHandler
99
158
  {
100
159
  private readonly string _stdout;
@@ -134,8 +193,7 @@ public class CloneWorkerTests
134
193
  // arrange
135
194
  var testApiHandler = new TestApiHandler();
136
195
  testGitCommandHandler ??= new TestGitCommandHandler();
137
- var testLogger = new TestLogger();
138
- var worker = new CloneWorker(testApiHandler, testGitCommandHandler, testLogger);
196
+ var worker = new CloneWorker("TEST-JOB-ID", testApiHandler, testGitCommandHandler);
139
197
 
140
198
  // act
141
199
  var job = new Job()
@@ -45,7 +45,16 @@ public class DiscoveryWorkerTestBase : TestBase
45
45
  ValidateProjectResults(expectedResult.Projects, actualResult.Projects, experimentsManager);
46
46
  Assert.Equal(expectedResult.ExpectedProjectCount ?? expectedResult.Projects.Length, actualResult.Projects.Length);
47
47
  Assert.Equal(expectedResult.ErrorType, actualResult.ErrorType);
48
- Assert.Equal(expectedResult.ErrorDetails, actualResult.ErrorDetails);
48
+ if (expectedResult.ErrorDetailsPattern is not null)
49
+ {
50
+ var errorDetails = actualResult.ErrorDetails?.ToString();
51
+ Assert.NotNull(errorDetails);
52
+ Assert.Matches(expectedResult.ErrorDetailsPattern, errorDetails);
53
+ }
54
+ else
55
+ {
56
+ Assert.Equal(expectedResult.ErrorDetails, actualResult.ErrorDetails);
57
+ }
49
58
 
50
59
  return;
51
60
 
@@ -115,5 +115,61 @@ public partial class DiscoveryWorkerTests
115
115
  }
116
116
  );
117
117
  }
118
+
119
+ [Theory]
120
+ [InlineData(true)]
121
+ [InlineData(false)]
122
+ public async Task DiscoveryIsMergedWithPackageReferences(bool useDirectDiscovery)
123
+ {
124
+ await TestDiscoveryAsync(
125
+ experimentsManager: new ExperimentsManager() { UseDirectDiscovery = useDirectDiscovery },
126
+ packages:
127
+ [
128
+ MockNuGetPackage.CreateSimplePackage("Package.A", "1.0.0", "net46"),
129
+ MockNuGetPackage.CreateSimplePackage("Package.B", "2.0.0", "net46"),
130
+ ],
131
+ workspacePath: "src",
132
+ files: [
133
+ ("src/myproj.csproj", """
134
+ <Project Sdk="Microsoft.NET.Sdk">
135
+ <PropertyGroup>
136
+ <TargetFramework>net46</TargetFramework>
137
+ </PropertyGroup>
138
+ <ItemGroup>
139
+ <None Include="..\unexpected-directory\packages.config" />
140
+ <PackageReference Include="Package.B" Version="2.0.0" />
141
+ </ItemGroup>
142
+ </Project>
143
+ """),
144
+ ("unexpected-directory/packages.config", """
145
+ <?xml version="1.0" encoding="utf-8"?>
146
+ <packages>
147
+ <package id="Package.A" version="1.0.0" targetFramework="net46" />
148
+ </packages>
149
+ """),
150
+ ],
151
+ expectedResult: new()
152
+ {
153
+ Path = "src",
154
+ Projects = [
155
+ new()
156
+ {
157
+ FilePath = "myproj.csproj",
158
+ Properties = [new("TargetFramework", "net46", "src/myproj.csproj")],
159
+ TargetFrameworks = ["net46"],
160
+ Dependencies = [
161
+ new("Package.A", "1.0.0", DependencyType.PackagesConfig, TargetFrameworks: ["net46"]),
162
+ new("Package.B", "2.0.0", DependencyType.PackageReference, IsDirect: true, TargetFrameworks: ["net46"]),
163
+ ],
164
+ ReferencedProjectPaths = [],
165
+ ImportedFiles = [],
166
+ AdditionalFiles = [
167
+ "../unexpected-directory/packages.config"
168
+ ],
169
+ }
170
+ ],
171
+ }
172
+ );
173
+ }
118
174
  }
119
175
  }
@@ -12,6 +12,7 @@ public record ExpectedWorkspaceDiscoveryResult : NativeResult
12
12
  public int? ExpectedProjectCount { get; init; }
13
13
  public ExpectedDependencyDiscoveryResult? GlobalJson { get; init; }
14
14
  public ExpectedDependencyDiscoveryResult? DotNetToolsJson { get; init; }
15
+ public string? ErrorDetailsPattern { get; init; } = null;
15
16
  }
16
17
 
17
18
  public record ExpectedSdkProjectDiscoveryResult : ExpectedDependencyDiscoveryResult
@@ -1733,7 +1733,7 @@ public class RunWorkerTests
1733
1733
  analyzeWorker ??= new AnalyzeWorker(experimentsManager, logger);
1734
1734
  updaterWorker ??= new UpdaterWorker(experimentsManager, logger);
1735
1735
 
1736
- var worker = new RunWorker(testApiHandler, discoveryWorker, analyzeWorker, updaterWorker, logger);
1736
+ var worker = new RunWorker("TEST-JOB-ID", testApiHandler, discoveryWorker, analyzeWorker, updaterWorker, logger);
1737
1737
  var repoContentsPathDirectoryInfo = new DirectoryInfo(tempDirectory.DirectoryPath);
1738
1738
  var actualResult = await worker.RunAsync(job, repoContentsPathDirectoryInfo, "TEST-COMMIT-SHA");
1739
1739
  var actualApiMessages = testApiHandler.ReceivedMessages.ToArray();
@@ -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: