dependabot-nuget 0.311.0 → 0.312.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 (26) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Run.cs +2 -2
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +7 -0
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/BadResponseException.cs +12 -0
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +121 -9
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +7 -0
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UnknownError.cs +12 -2
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/HttpApiHandler.cs +19 -50
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/IApiHandler.cs +33 -7
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +10 -5
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +13 -1
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +1 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/VersionFinderTests.cs +74 -1
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Clone/CloneWorkerTests.cs +1 -1
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +3 -9
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +1 -2
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +58 -2
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/HttpApiHandlerTests.cs +116 -0
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MessageReportTests.cs +1 -2
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +227 -6
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +40 -11
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/TestApiHandler.cs +2 -39
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestHttpServer.cs +9 -5
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackageReferenceUpdaterTests.cs +99 -1
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +8 -0
  26. metadata +25 -23
@@ -832,10 +832,8 @@ public partial class DiscoveryWorkerTests
832
832
  {
833
833
  FilePath = "myproj.csproj",
834
834
  Dependencies = [
835
- new("Some.Package", "1.2.3.4", DependencyType.PackageReference, TargetFrameworks: ["net7.0"], IsDirect: true),
836
- new("Some.Package", "1.2.3.4", DependencyType.PackageReference, TargetFrameworks: ["net8.0"], IsDirect: true),
837
- new("Transitive.Dependency", "5.6.7.8", DependencyType.Unknown, TargetFrameworks: ["net7.0"], IsTransitive: true),
838
- new("Transitive.Dependency", "5.6.7.8", DependencyType.Unknown, TargetFrameworks: ["net8.0"], IsTransitive: true),
835
+ new("Some.Package", "1.2.3.4", DependencyType.PackageReference, TargetFrameworks: ["net7.0", "net8.0"], IsDirect: true),
836
+ new("Transitive.Dependency", "5.6.7.8", DependencyType.Unknown, TargetFrameworks: ["net7.0", "net8.0"], IsTransitive: true),
839
837
  ],
840
838
  Properties = [
841
839
  new("TargetFrameworks", "net7.0;net8.0", "myproj.csproj"),
@@ -1191,11 +1189,7 @@ public partial class DiscoveryWorkerTests
1191
1189
  {
1192
1190
  FilePath = "project.csproj",
1193
1191
  Dependencies = [
1194
- new("Some.Package", "1.2.3", DependencyType.PackageReference, TargetFrameworks: ["net8.0-android"], IsDirect: true),
1195
- new("Some.Package", "1.2.3", DependencyType.PackageReference, TargetFrameworks: ["net8.0-ios"], IsDirect: true),
1196
- new("Some.Package", "1.2.3", DependencyType.PackageReference, TargetFrameworks: ["net8.0-maccatalyst"], IsDirect: true),
1197
- new("Some.Package", "1.2.3", DependencyType.PackageReference, TargetFrameworks: ["net8.0-macos"], IsDirect: true),
1198
- new("Some.Package", "1.2.3", DependencyType.PackageReference, TargetFrameworks: ["net8.0-windows"], IsDirect: true),
1192
+ new("Some.Package", "1.2.3", DependencyType.PackageReference, TargetFrameworks: ["net8.0-android", "net8.0-ios", "net8.0-maccatalyst", "net8.0-macos", "net8.0-windows"], IsDirect: true),
1199
1193
  ],
1200
1194
  Properties = [
1201
1195
  new("TargetFrameworks", "net8.0-ios;net8.0-android;net8.0-macos;net8.0-maccatalyst;net8.0-windows", @"src/project.csproj"),
@@ -673,8 +673,7 @@ public partial class DiscoveryWorkerTests : DiscoveryWorkerTestBase
673
673
  FilePath = "src/project.csproj",
674
674
  TargetFrameworks = ["net7.0", "net8.0"],
675
675
  Dependencies = [
676
- new("Some.Package", "9.0.1", DependencyType.PackageReference, TargetFrameworks: ["net7.0"], IsDirect: true),
677
- new("Some.Package", "9.0.1", DependencyType.PackageReference, TargetFrameworks: ["net8.0"], IsDirect: true),
676
+ new("Some.Package", "9.0.1", DependencyType.PackageReference, TargetFrameworks: ["net7.0", "net8.0"], IsDirect: true),
678
677
  ],
679
678
  Properties = [
680
679
  new("TargetFrameworks", "net7.0;net8.0", "src/project.csproj")
@@ -1,4 +1,5 @@
1
1
  using System.Collections.Immutable;
2
+ using System.Text;
2
3
 
3
4
  using NuGetUpdater.Core.Discover;
4
5
  using NuGetUpdater.Core.Test.Update;
@@ -297,8 +298,7 @@ public class SdkProjectDiscoveryTests : DiscoveryWorkerTestBase
297
298
  FilePath = "library.csproj",
298
299
  Dependencies =
299
300
  [
300
- new("Some.Dependency", "1.2.3", DependencyType.PackageReference, TargetFrameworks: ["net7.0"], IsDirect: true),
301
- new("Some.Dependency", "1.2.3", DependencyType.PackageReference, TargetFrameworks: ["net8.0"], IsDirect: true),
301
+ new("Some.Dependency", "1.2.3", DependencyType.PackageReference, TargetFrameworks: ["net7.0", "net8.0"], IsDirect: true),
302
302
  ],
303
303
  ImportedFiles = [],
304
304
  Properties =
@@ -524,6 +524,62 @@ public class SdkProjectDiscoveryTests : DiscoveryWorkerTestBase
524
524
  );
525
525
  }
526
526
 
527
+ [Fact]
528
+ public async Task TransitiveDependenciesWithoutAssembliesAreReported()
529
+ {
530
+ await TestDiscoverAsync(
531
+ packages:
532
+ [
533
+ MockNuGetPackage.CreateSimplePackage("Some.Dependency", "1.2.3", "net9.0", [(null, [("Transitive.Dependency", "4.5.6")])]),
534
+ new MockNuGetPackage(
535
+ "Transitive.Dependency",
536
+ "4.5.6",
537
+ Files: [
538
+ ("build/Transitive.Dependency.targets", Encoding.UTF8.GetBytes("<Project />"))
539
+ ],
540
+ DependencyGroups: [(null, [("Super.Transitive.Dependency", "7.8.9")])]
541
+ ),
542
+ MockNuGetPackage.CreateSimplePackage("Super.Transitive.Dependency", "7.8.9", "net9.0"),
543
+ ],
544
+ startingDirectory: "src",
545
+ projectPath: "src/library.csproj",
546
+ files:
547
+ [
548
+ ("src/library.csproj", """
549
+ <Project Sdk="Microsoft.NET.Sdk">
550
+ <PropertyGroup>
551
+ <TargetFramework>net9.0</TargetFramework>
552
+ </PropertyGroup>
553
+ <ItemGroup>
554
+ <PackageReference Include="Some.Dependency" Version="1.2.3" />
555
+ </ItemGroup>
556
+ </Project>
557
+ """)
558
+ ],
559
+ expectedProjects:
560
+ [
561
+ new()
562
+ {
563
+ FilePath = "library.csproj",
564
+ Dependencies =
565
+ [
566
+ new("Some.Dependency", "1.2.3", DependencyType.PackageReference, TargetFrameworks: ["net9.0"], IsDirect: true),
567
+ new("Transitive.Dependency", "4.5.6", DependencyType.Unknown, TargetFrameworks: ["net9.0"], IsTransitive: true),
568
+ new("Super.Transitive.Dependency", "7.8.9", DependencyType.Unknown, TargetFrameworks: ["net9.0"], IsTransitive: true),
569
+ ],
570
+ ImportedFiles = [],
571
+ Properties =
572
+ [
573
+ new("TargetFramework", "net9.0", "src/library.csproj"),
574
+ ],
575
+ TargetFrameworks = ["net9.0"],
576
+ ReferencedProjectPaths = [],
577
+ AdditionalFiles = [],
578
+ },
579
+ ]
580
+ );
581
+ }
582
+
527
583
  private static async Task TestDiscoverAsync(string startingDirectory, string projectPath, TestFile[] files, ImmutableArray<ExpectedSdkProjectDiscoveryResult> expectedProjects, MockNuGetPackage[]? packages = null)
528
584
  {
529
585
  using var testDirectory = await TemporaryDirectory.CreateWithContentsAsync(files);
@@ -0,0 +1,116 @@
1
+ using NuGetUpdater.Core.Run;
2
+ using NuGetUpdater.Core.Run.ApiModel;
3
+ using NuGetUpdater.Core.Test.Utilities;
4
+
5
+ using Xunit;
6
+
7
+ namespace NuGetUpdater.Core.Test.Run;
8
+
9
+ public class HttpApiHandlerTests
10
+ {
11
+ [Fact]
12
+ public async Task FailedRequestWithContentReportsData()
13
+ {
14
+ // arrange
15
+ // this mimics an error that can be returned by the server
16
+ var errorContent = """{"errors":[{"status":400,"title":"Bad Request","detail":"some-detail"}]}""";
17
+ using var http = TestHttpServer.CreateTestStringServer((method, url) =>
18
+ {
19
+ return (400, errorContent);
20
+ });
21
+ var handler = new HttpApiHandler(http.BaseUrl, "TEST-ID");
22
+
23
+ // act
24
+ var exception = await Assert.ThrowsAsync<HttpRequestException>(() => handler.IncrementMetric(new()
25
+ {
26
+ // body is irrelevant for this test
27
+ Metric = "TEST",
28
+ }));
29
+
30
+ // assert
31
+ var expectedMessage = $"400 (BadRequest): {errorContent}";
32
+ Assert.Equal(expectedMessage, exception.Message);
33
+ }
34
+
35
+ [Fact]
36
+ public async Task FailedRequestWithNoContentOnlyReportsStatusCode()
37
+ {
38
+ // arrange
39
+ using var http = TestHttpServer.CreateTestServer((method, url) =>
40
+ {
41
+ // no error content returned
42
+ return (500, null);
43
+ });
44
+ var handler = new HttpApiHandler(http.BaseUrl, "TEST-ID");
45
+
46
+ // act
47
+ var exception = await Assert.ThrowsAsync<HttpRequestException>(() => handler.IncrementMetric(new()
48
+ {
49
+ // body is irrelevant for this test
50
+ Metric = "TEST",
51
+ }));
52
+
53
+ // assert
54
+ var expectedMessage = $"500 (InternalServerError)";
55
+ Assert.Equal(expectedMessage, exception.Message);
56
+ }
57
+
58
+ [Theory]
59
+ [MemberData(nameof(ErrorsAreSentToTheCorrectEndpointTestData))]
60
+ public async Task ErrorsAreSentToTheCorrectEndpoint(JobErrorBase error, params string[] expectedEndpoints)
61
+ {
62
+ // arrange
63
+ var actualEndpoints = new List<string>();
64
+ using var http = TestHttpServer.CreateTestStringServer((method, url) =>
65
+ {
66
+ var expectedPrefix = "/update_jobs/TEST-ID/";
67
+ var actualPathAndQuery = new Uri(url).PathAndQuery;
68
+ if (!actualPathAndQuery.StartsWith(expectedPrefix))
69
+ {
70
+ throw new Exception($"Didn't find expected prefix: [{expectedPrefix}]");
71
+ }
72
+
73
+ actualEndpoints.Add(actualPathAndQuery[expectedPrefix.Length..]);
74
+ return (200, "ok");
75
+ });
76
+ var handler = new HttpApiHandler(http.BaseUrl, "TEST-ID");
77
+
78
+ // act
79
+ await handler.RecordUpdateJobError(error);
80
+
81
+ // assert
82
+ AssertEx.Equal(expectedEndpoints, actualEndpoints);
83
+ }
84
+
85
+ [Fact]
86
+ public void ErrorsAreSentToTheCorrectEndpoint_AllTypesAreTested()
87
+ {
88
+ var remainingErrorTypes = typeof(JobErrorBase).Assembly
89
+ .GetTypes()
90
+ .Where(t => t.IsSubclassOf(typeof(JobErrorBase)))
91
+ .Select(t => t.Name)
92
+ .ToHashSet();
93
+ foreach (var testData in ErrorsAreSentToTheCorrectEndpointTestData())
94
+ {
95
+ var seenErrorType = testData[0].GetType().Name;
96
+ remainingErrorTypes.Remove(seenErrorType);
97
+ }
98
+
99
+ Assert.Empty(remainingErrorTypes);
100
+ }
101
+
102
+ public static IEnumerable<object[]> ErrorsAreSentToTheCorrectEndpointTestData()
103
+ {
104
+ yield return [new BadRequirement("unused"), "record_update_job_error"];
105
+ yield return [new DependencyFileNotFound("unused"), "record_update_job_error"];
106
+ yield return [new DependencyFileNotParseable("unused"), "record_update_job_error"];
107
+ yield return [new DependencyNotFound("unused"), "record_update_job_error"];
108
+ yield return [new JobRepoNotFound("unused"), "record_update_job_error"];
109
+ yield return [new PrivateSourceAuthenticationFailure(["unused"]), "record_update_job_error"];
110
+ yield return [new PrivateSourceBadResponse(["unused"]), "record_update_job_error"];
111
+ yield return [new PullRequestExistsForLatestVersion("unused", "unused"), "record_update_job_error"];
112
+ yield return [new SecurityUpdateNotNeeded("unused"), "record_update_job_error"];
113
+ yield return [new UnknownError(new Exception("unused"), "unused"), "record_update_job_error", "record_update_job_unknown_error", "increment_metric"];
114
+ yield return [new UpdateNotPossible(["unused"]), "record_update_job_error"];
115
+ }
116
+ }
@@ -190,8 +190,7 @@ public class MessageReportTests
190
190
  """
191
191
  Error type: unknown_error
192
192
  - error-class: NotImplementedException
193
- - error-message: error message
194
- - error-backtrace: <unknown>
193
+ - error-message: System.NotImplementedException: error message
195
194
  - package-manager: nuget
196
195
  - job-id: TEST-JOB-ID
197
196
  """
@@ -16,8 +16,6 @@ using Xunit;
16
16
 
17
17
  namespace NuGetUpdater.Core.Test.Run;
18
18
 
19
- using static NuGetUpdater.Core.Utilities.EOLHandling;
20
-
21
19
  using TestFile = (string Path, string Content);
22
20
  using RawTestFile = (string Path, byte[] Content);
23
21
 
@@ -2777,6 +2775,162 @@ public class RunWorkerTests
2777
2775
  );
2778
2776
  }
2779
2777
 
2778
+ [Fact]
2779
+ public async Task AnalysisResultWithoutUpdatedDependenciesDoesNotCauseError()
2780
+ {
2781
+ await RunAsync(
2782
+ job: new()
2783
+ {
2784
+ Source = new()
2785
+ {
2786
+ Provider = "github",
2787
+ Repo = "test/repo",
2788
+ },
2789
+ Dependencies = [
2790
+ "Some.Package"
2791
+ ],
2792
+ },
2793
+ files: [
2794
+ ("project.csproj", "contents irrelevant")
2795
+ ],
2796
+ discoveryWorker: new TestDiscoveryWorker(_input =>
2797
+ {
2798
+ return Task.FromResult(new WorkspaceDiscoveryResult()
2799
+ {
2800
+ Path = "",
2801
+ Projects = [
2802
+ new()
2803
+ {
2804
+ FilePath = "project.csproj",
2805
+ Dependencies = [
2806
+ new("Some.Package", "1.0.0", DependencyType.PackageReference)
2807
+ ],
2808
+ ImportedFiles = [],
2809
+ AdditionalFiles = [],
2810
+ }
2811
+ ]
2812
+ });
2813
+ }),
2814
+ analyzeWorker: new TestAnalyzeWorker(_input =>
2815
+ {
2816
+ return Task.FromResult(new AnalysisResult()
2817
+ {
2818
+ CanUpdate = true,
2819
+ UpdatedVersion = "1.1.0",
2820
+ UpdatedDependencies = [], // this is what was causing the problem
2821
+ });
2822
+ }),
2823
+ updaterWorker: new TestUpdaterWorker(async input =>
2824
+ {
2825
+ var repoRootPath = input.Item1;
2826
+ var filePath = input.Item2;
2827
+ await File.WriteAllTextAsync(Path.Join(repoRootPath, filePath), "updated contents irrelevant");
2828
+ return new UpdateOperationResult()
2829
+ {
2830
+ UpdateOperations = [
2831
+ new DirectUpdate()
2832
+ {
2833
+ DependencyName = "Some.Package",
2834
+ NewVersion = NuGetVersion.Parse("1.1.0"),
2835
+ UpdatedFiles = ["project.csproj"]
2836
+ }
2837
+ ]
2838
+ };
2839
+ }),
2840
+ expectedResult: new()
2841
+ {
2842
+ Base64DependencyFiles = [
2843
+ new()
2844
+ {
2845
+ Directory = "/",
2846
+ Name = "project.csproj",
2847
+ Content = Convert.ToBase64String(Encoding.UTF8.GetBytes("contents irrelevant")),
2848
+ ContentEncoding = "base64"
2849
+ }
2850
+ ],
2851
+ BaseCommitSha = "TEST-COMMIT-SHA",
2852
+ },
2853
+ expectedApiMessages: [
2854
+ new UpdatedDependencyList()
2855
+ {
2856
+ Dependencies = [
2857
+ new()
2858
+ {
2859
+ Name = "Some.Package",
2860
+ Version = "1.0.0",
2861
+ Requirements = [
2862
+ new()
2863
+ {
2864
+ Requirement = "1.0.0",
2865
+ File = "/project.csproj",
2866
+ Groups = ["dependencies"],
2867
+ }
2868
+ ]
2869
+ }
2870
+ ],
2871
+ DependencyFiles = ["/project.csproj"]
2872
+ },
2873
+ new IncrementMetric()
2874
+ {
2875
+ Metric = "updater.started",
2876
+ Tags = new()
2877
+ {
2878
+ ["operation"] = "group_update_all_versions"
2879
+ }
2880
+ },
2881
+ new CreatePullRequest()
2882
+ {
2883
+ Dependencies =
2884
+ [
2885
+ new ReportedDependency()
2886
+ {
2887
+ Name = "Some.Package",
2888
+ Version = "1.1.0",
2889
+ Requirements =
2890
+ [
2891
+ new ReportedRequirement()
2892
+ {
2893
+ Requirement = "1.1.0",
2894
+ File = "/project.csproj",
2895
+ Groups = ["dependencies"],
2896
+ Source = new()
2897
+ {
2898
+ SourceUrl = null,
2899
+ Type = "nuget_repo",
2900
+ }
2901
+ }
2902
+ ],
2903
+ PreviousVersion = "1.0.0",
2904
+ PreviousRequirements =
2905
+ [
2906
+ new ReportedRequirement()
2907
+ {
2908
+ Requirement = "1.0.0",
2909
+ File = "/project.csproj",
2910
+ Groups = ["dependencies"],
2911
+ }
2912
+ ],
2913
+ }
2914
+ ],
2915
+ UpdatedDependencyFiles =
2916
+ [
2917
+ new DependencyFile()
2918
+ {
2919
+ Name = "project.csproj",
2920
+ Directory = "/",
2921
+ Content = "updated contents irrelevant",
2922
+ },
2923
+ ],
2924
+ BaseCommitSha = "TEST-COMMIT-SHA",
2925
+ CommitMessage = TestPullRequestCommitMessage,
2926
+ PrTitle = TestPullRequestTitle,
2927
+ PrBody = TestPullRequestBody,
2928
+ },
2929
+ new MarkAsProcessed("TEST-COMMIT-SHA"),
2930
+ ]
2931
+ );
2932
+ }
2933
+
2780
2934
  [Fact]
2781
2935
  public async Task ByteOrderMarksAreDetectedAndRestored()
2782
2936
  {
@@ -3266,6 +3420,50 @@ public class RunWorkerTests
3266
3420
  );
3267
3421
  }
3268
3422
 
3423
+ [Fact]
3424
+ public async Task UnknownErrorsGenerateAllRequiredApiCalls()
3425
+ {
3426
+ await RunAsync(
3427
+ job: new Job()
3428
+ {
3429
+ Source = new()
3430
+ {
3431
+ Provider = "github",
3432
+ Repo = "test/repo",
3433
+ Directory = "some-dir",
3434
+ }
3435
+ },
3436
+ packages: [],
3437
+ files: [],
3438
+ discoveryWorker: new TestDiscoveryWorker(_input =>
3439
+ {
3440
+ throw new FileNotFoundException("some required file is missing");
3441
+ }),
3442
+ analyzeWorker: TestAnalyzeWorker.FromResults(), // unreachable
3443
+ updaterWorker: TestUpdaterWorker.FromResults(), // unreachable
3444
+ expectedResult: new RunResult()
3445
+ {
3446
+ Base64DependencyFiles = [],
3447
+ BaseCommitSha = "TEST-COMMIT-SHA",
3448
+ },
3449
+ expectedApiMessages:
3450
+ [
3451
+ new UnknownError(new FileNotFoundException("some required file is missing"), "TEST-JOB-ID"), // from record_update_job_error
3452
+ new UnknownError(new FileNotFoundException("some required file is missing"), "TEST-JOB-ID"), // from record_update_job_unknown_error
3453
+ new IncrementMetric()
3454
+ {
3455
+ Metric = "updater.update_job_unknown_error",
3456
+ Tags = new()
3457
+ {
3458
+ ["package_manager"] = "nuget",
3459
+ ["class_name"] = "FileNotFoundException"
3460
+ }
3461
+ },
3462
+ new MarkAsProcessed("TEST-COMMIT-SHA")
3463
+ ]
3464
+ );
3465
+ }
3466
+
3269
3467
  internal static Task RunAsync(Job job, TestFile[] files, IDiscoveryWorker? discoveryWorker, IAnalyzeWorker? analyzeWorker, IUpdaterWorker? updaterWorker, RunResult expectedResult, object[] expectedApiMessages, MockNuGetPackage[]? packages = null, ExperimentsManager? experimentsManager = null, string? repoContentsPath = null)
3270
3468
  {
3271
3469
  var rawTestFiles = files.Select(f => (f.Path, Encoding.UTF8.GetBytes(f.Content))).ToArray();
@@ -3300,13 +3498,36 @@ public class RunWorkerTests
3300
3498
  var actualResult = await worker.RunAsync(job, repoContentsPathDirectoryInfo, "TEST-COMMIT-SHA");
3301
3499
  var actualApiMessages = testApiHandler.ReceivedMessages
3302
3500
  .Select(m =>
3303
- m.Object switch
3501
+ {
3502
+ object newObject;
3503
+ switch (m.Object)
3304
3504
  {
3305
3505
  // this isn't the place to verify the generated text
3306
- CreatePullRequest create => (m.Type, create with { CommitMessage = TestPullRequestCommitMessage, PrTitle = TestPullRequestTitle, PrBody = TestPullRequestBody }),
3307
- UpdatePullRequest update => (m.Type, update with { CommitMessage = TestPullRequestCommitMessage, PrTitle = TestPullRequestTitle, PrBody = TestPullRequestBody }),
3308
- _ => m,
3506
+ case CreatePullRequest create:
3507
+ newObject = create with { CommitMessage = TestPullRequestCommitMessage, PrTitle = TestPullRequestTitle, PrBody = TestPullRequestBody };
3508
+ break;
3509
+ case UpdatePullRequest update:
3510
+ newObject = update with { CommitMessage = TestPullRequestCommitMessage, PrTitle = TestPullRequestTitle, PrBody = TestPullRequestBody };
3511
+ break;
3512
+ // don't test callstacks
3513
+ case UnknownError unknown:
3514
+ var message = (string)unknown.Details["error-message"];
3515
+ var stackTraceOffset = message.IndexOf('\n');
3516
+ if (stackTraceOffset >= 0)
3517
+ {
3518
+ var messageWithoutStackTrace = message[..stackTraceOffset].TrimEnd('\r');
3519
+ unknown.Details["error-message"] = messageWithoutStackTrace;
3520
+ }
3521
+
3522
+ newObject = unknown;
3523
+ break;
3524
+ default:
3525
+ newObject = m.Object;
3526
+ break;
3309
3527
  }
3528
+
3529
+ return (m.Type, Object: newObject);
3530
+ }
3310
3531
  ).ToArray();
3311
3532
 
3312
3533
  // assert
@@ -8,7 +8,7 @@ using Xunit;
8
8
 
9
9
  namespace NuGetUpdater.Core.Test.Run;
10
10
 
11
- public class SerializationTests
11
+ public class SerializationTests : TestBase
12
12
  {
13
13
  [Fact]
14
14
  public void DeserializeJob()
@@ -260,15 +260,9 @@ public class SerializationTests
260
260
  }
261
261
 
262
262
  [Theory]
263
- [MemberData(nameof(DeserializeErrorTypesData))]
263
+ [MemberData(nameof(SerializeErrorTypesData))]
264
264
  public void SerializeError(JobErrorBase error, string expectedSerialization)
265
265
  {
266
- if (error is UnknownError unknown)
267
- {
268
- // special case the exception's call stack to make it testable
269
- unknown.Details["error-backtrace"] = "TEST-BACKTRACE";
270
- }
271
-
272
266
  var actual = HttpApiHandler.Serialize(error);
273
267
  Assert.Equal(expectedSerialization, actual);
274
268
  }
@@ -279,7 +273,7 @@ public class SerializationTests
279
273
  var untestedTypes = typeof(JobErrorBase).Assembly.GetTypes()
280
274
  .Where(t => t.IsSubclassOf(typeof(JobErrorBase)))
281
275
  .ToHashSet();
282
- foreach (object?[] data in DeserializeErrorTypesData())
276
+ foreach (object?[] data in SerializeErrorTypesData())
283
277
  {
284
278
  var testedErrorType = data[0]!.GetType();
285
279
  untestedTypes.Remove(testedErrorType);
@@ -601,7 +595,42 @@ public class SerializationTests
601
595
  Assert.Equal(expected, actual);
602
596
  }
603
597
 
604
- public static IEnumerable<object?[]> DeserializeErrorTypesData()
598
+ [Fact]
599
+ public void SerializeRealUnknownErrorWithInnerException()
600
+ {
601
+ // arrange
602
+ using var tempDir = new TemporaryDirectory();
603
+ var action = new Action(() =>
604
+ {
605
+ try
606
+ {
607
+ throw new NotImplementedException("inner message");
608
+ }
609
+ catch (Exception ex)
610
+ {
611
+ throw new InvalidOperationException("outer message", ex);
612
+ }
613
+ });
614
+ var ex = Assert.Throws<InvalidOperationException>(action);
615
+
616
+ // act
617
+ var error = JobErrorBase.ErrorFromException(ex, "TEST-JOB-ID", tempDir.DirectoryPath);
618
+
619
+ // assert
620
+ // real exception message should look like this:
621
+ // System.InvalidOperationException: outer message
622
+ // ---> System.NotImplementedException: inner message
623
+ // at Namespace.Class.Method() in file.cs:line 123
624
+ // --- End of inner exception stack trace ---
625
+ // at Namespace.Class.Method() in file.cs:line 456
626
+ var errorMessage = Assert.IsType<string>(error.Details["error-message"]);
627
+ var lines = errorMessage.Split('\n').Select(l => l.TrimEnd('\r')).ToArray();
628
+ Assert.Equal("System.InvalidOperationException: outer message", lines[0]);
629
+ Assert.Equal(" ---> System.NotImplementedException: inner message", lines[1]);
630
+ Assert.Contains(" --- End of inner exception stack trace ---", lines[2..]);
631
+ }
632
+
633
+ public static IEnumerable<object?[]> SerializeErrorTypesData()
605
634
  {
606
635
  yield return
607
636
  [
@@ -679,7 +708,7 @@ public class SerializationTests
679
708
  [
680
709
  new UnknownError(new Exception("some message"), "JOB-ID"),
681
710
  """
682
- {"data":{"error-type":"unknown_error","error-details":{"error-class":"Exception","error-message":"some message","error-backtrace":"TEST-BACKTRACE","package-manager":"nuget","job-id":"JOB-ID"}}}
711
+ {"data":{"error-type":"unknown_error","error-details":{"error-class":"Exception","error-message":"System.Exception: some message","error-backtrace":"","package-manager":"nuget","job-id":"JOB-ID"}}}
683
712
  """
684
713
  ];
685
714
 
@@ -1,5 +1,4 @@
1
1
  using NuGetUpdater.Core.Run;
2
- using NuGetUpdater.Core.Run.ApiModel;
3
2
 
4
3
  namespace NuGetUpdater.Core.Test;
5
4
 
@@ -9,45 +8,9 @@ internal class TestApiHandler : IApiHandler
9
8
 
10
9
  public IEnumerable<(Type Type, object Object)> ReceivedMessages => _receivedMessages;
11
10
 
12
- public Task RecordUpdateJobError(JobErrorBase error)
11
+ public Task SendAsync(string endpoint, object body, string method)
13
12
  {
14
- _receivedMessages.Add((error.GetType(), error));
15
- return Task.CompletedTask;
16
- }
17
-
18
- public Task UpdateDependencyList(UpdatedDependencyList updatedDependencyList)
19
- {
20
- _receivedMessages.Add((updatedDependencyList.GetType(), updatedDependencyList));
21
- return Task.CompletedTask;
22
- }
23
-
24
- public Task IncrementMetric(IncrementMetric incrementMetric)
25
- {
26
- _receivedMessages.Add((incrementMetric.GetType(), incrementMetric));
27
- return Task.CompletedTask;
28
- }
29
-
30
- public Task CreatePullRequest(CreatePullRequest createPullRequest)
31
- {
32
- _receivedMessages.Add((createPullRequest.GetType(), createPullRequest));
33
- return Task.CompletedTask;
34
- }
35
-
36
- public Task ClosePullRequest(ClosePullRequest closePullRequest)
37
- {
38
- _receivedMessages.Add((closePullRequest.GetType(), closePullRequest));
39
- return Task.CompletedTask;
40
- }
41
-
42
- public Task UpdatePullRequest(UpdatePullRequest updatePullRequest)
43
- {
44
- _receivedMessages.Add((updatePullRequest.GetType(), updatePullRequest));
45
- return Task.CompletedTask;
46
- }
47
-
48
- public Task MarkAsProcessed(MarkAsProcessed markAsProcessed)
49
- {
50
- _receivedMessages.Add((markAsProcessed.GetType(), markAsProcessed));
13
+ _receivedMessages.Add((body.GetType(), body));
51
14
  return Task.CompletedTask;
52
15
  }
53
16
  }