up
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				Build Test Deploy / docs (push) Has been cancelled
				
			
		
			
				
	
				Build Test Deploy / deploy (push) Has been cancelled
				
			
		
			
				
	
				Build Test Deploy / build-test (push) Has been cancelled
				
			
		
			
				
	
				Build Test Deploy / authority-container (push) Has been cancelled
				
			
		
			
				
	
				Docs CI / lint-and-preview (push) Has been cancelled
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	Build Test Deploy / docs (push) Has been cancelled
				
			Build Test Deploy / deploy (push) Has been cancelled
				
			Build Test Deploy / build-test (push) Has been cancelled
				
			Build Test Deploy / authority-container (push) Has been cancelled
				
			Docs CI / lint-and-preview (push) Has been cancelled
				
			This commit is contained in:
		| @@ -35,12 +35,14 @@ public sealed class CertCcConnectorSnapshotTests : IAsyncLifetime | ||||
| { | ||||
|     private static readonly Uri SeptemberSummaryUri = new("https://www.kb.cert.org/vuls/api/2025/09/summary/"); | ||||
|     private static readonly Uri OctoberSummaryUri = new("https://www.kb.cert.org/vuls/api/2025/10/summary/"); | ||||
|     private static readonly Uri YearlySummaryUri = new("https://www.kb.cert.org/vuls/api/2025/summary/"); | ||||
|     private static readonly Uri NovemberSummaryUri = new("https://www.kb.cert.org/vuls/api/2025/11/summary/"); | ||||
|     private static readonly Uri NoteDetailUri = new("https://www.kb.cert.org/vuls/api/294418/"); | ||||
|     private static readonly Uri VendorsDetailUri = new("https://www.kb.cert.org/vuls/api/294418/vendors/"); | ||||
|     private static readonly Uri VulsDetailUri = new("https://www.kb.cert.org/vuls/api/294418/vuls/"); | ||||
|     private static readonly Uri VendorStatusesDetailUri = new("https://www.kb.cert.org/vuls/api/294418/vendors/vuls/"); | ||||
|  | ||||
|     private static readonly Uri YearlySummaryUri = new("https://www.kb.cert.org/vuls/api/2025/summary/"); | ||||
|  | ||||
|     private readonly MongoIntegrationFixture _fixture; | ||||
|     private ConnectorTestHarness? _harness; | ||||
|  | ||||
| @@ -75,9 +77,24 @@ public sealed class CertCcConnectorSnapshotTests : IAsyncLifetime | ||||
|  | ||||
|         harness.TimeProvider.Advance(TimeSpan.FromMinutes(30)); | ||||
|         RegisterSummaryNotModifiedResponses(harness.Handler); | ||||
|         RegisterDetailNotModifiedResponses(harness.Handler); | ||||
|  | ||||
|         await connector.FetchAsync(harness.ServiceProvider, CancellationToken.None); | ||||
|         var recordedRequests = harness.Handler.Requests | ||||
|             .Select(request => request.Uri.ToString()) | ||||
|             .ToArray(); | ||||
|         recordedRequests.Should().Equal(new[] | ||||
|         { | ||||
|             SeptemberSummaryUri.ToString(), | ||||
|             OctoberSummaryUri.ToString(), | ||||
|             NoteDetailUri.ToString(), | ||||
|             VendorsDetailUri.ToString(), | ||||
|             VulsDetailUri.ToString(), | ||||
|             VendorStatusesDetailUri.ToString(), | ||||
|             YearlySummaryUri.ToString(), | ||||
|             OctoberSummaryUri.ToString(), | ||||
|             NovemberSummaryUri.ToString(), | ||||
|             YearlySummaryUri.ToString(), | ||||
|         }); | ||||
|         harness.Handler.AssertNoPendingResponses(); | ||||
|  | ||||
|         var requestsSnapshot = BuildRequestsSnapshot(harness.Handler.Requests); | ||||
| @@ -128,7 +145,7 @@ public sealed class CertCcConnectorSnapshotTests : IAsyncLifetime | ||||
|         { | ||||
|             SeptemberSummaryUri, | ||||
|             OctoberSummaryUri, | ||||
|             new Uri("https://www.kb.cert.org/vuls/api/2025/11/summary/"), | ||||
|             NovemberSummaryUri, | ||||
|             YearlySummaryUri, | ||||
|             NoteDetailUri, | ||||
|             VendorsDetailUri, | ||||
| @@ -255,17 +272,13 @@ public sealed class CertCcConnectorSnapshotTests : IAsyncLifetime | ||||
|     { | ||||
|         AddJsonResponse(handler, SeptemberSummaryUri, "summary-2025-09.json", "\"certcc-summary-2025-09\"", new DateTimeOffset(2025, 9, 30, 12, 0, 0, TimeSpan.Zero)); | ||||
|         AddJsonResponse(handler, OctoberSummaryUri, "summary-2025-10.json", "\"certcc-summary-2025-10\"", new DateTimeOffset(2025, 10, 31, 12, 0, 0, TimeSpan.Zero)); | ||||
|         var novemberUri = new Uri("https://www.kb.cert.org/vuls/api/2025/11/summary/"); | ||||
|         AddJsonResponse(handler, novemberUri, "summary-2025-11.json", "\"certcc-summary-2025-11\"", new DateTimeOffset(2025, 11, 1, 8, 0, 0, TimeSpan.Zero)); | ||||
|         AddJsonResponse(handler, YearlySummaryUri, "summary-2025.json", "\"certcc-summary-2025\"", new DateTimeOffset(2025, 10, 31, 12, 1, 0, TimeSpan.Zero)); | ||||
|     } | ||||
|  | ||||
|     private static void RegisterSummaryNotModifiedResponses(CannedHttpMessageHandler handler) | ||||
|     { | ||||
|         AddNotModified(handler, SeptemberSummaryUri, "\"certcc-summary-2025-09\""); | ||||
|         AddNotModified(handler, OctoberSummaryUri, "\"certcc-summary-2025-10\""); | ||||
|         var novemberUri = new Uri("https://www.kb.cert.org/vuls/api/2025/11/summary/"); | ||||
|         AddNotModified(handler, novemberUri, "\"certcc-summary-2025-11\""); | ||||
|         AddNotModified(handler, NovemberSummaryUri, "\"certcc-summary-2025-11\""); | ||||
|         AddNotModified(handler, YearlySummaryUri, "\"certcc-summary-2025\""); | ||||
|     } | ||||
|  | ||||
| @@ -277,14 +290,6 @@ public sealed class CertCcConnectorSnapshotTests : IAsyncLifetime | ||||
|         AddJsonResponse(handler, VendorStatusesDetailUri, "vendor-statuses-294418.json", "\"certcc-vendor-statuses-294418\"", new DateTimeOffset(2025, 10, 9, 17, 12, 0, TimeSpan.Zero)); | ||||
|     } | ||||
|  | ||||
|     private static void RegisterDetailNotModifiedResponses(CannedHttpMessageHandler handler) | ||||
|     { | ||||
|         AddNotModified(handler, NoteDetailUri, "\"certcc-note-294418\""); | ||||
|         AddNotModified(handler, VendorsDetailUri, "\"certcc-vendors-294418\""); | ||||
|         AddNotModified(handler, VulsDetailUri, "\"certcc-vuls-294418\""); | ||||
|         AddNotModified(handler, VendorStatusesDetailUri, "\"certcc-vendor-statuses-294418\""); | ||||
|     } | ||||
|  | ||||
|     private static void AddJsonResponse(CannedHttpMessageHandler handler, Uri uri, string fixtureName, string etag, DateTimeOffset lastModified) | ||||
|     { | ||||
|         var payload = ReadFixture(fixtureName); | ||||
|   | ||||
| @@ -227,6 +227,46 @@ public sealed class CertCcConnectorTests : IAsyncLifetime | ||||
|         pendingSummaries.Should().Be(0); | ||||
|     } | ||||
|  | ||||
|     [Fact] | ||||
|     public async Task Fetch_PartialDetailEndpointsMissing_CompletesAndMaps() | ||||
|     { | ||||
|         await using var provider = await BuildServiceProviderAsync(); | ||||
|         SeedSummaryResponses(); | ||||
|         SeedDetailResponses( | ||||
|             vulsStatus: HttpStatusCode.NotFound, | ||||
|             vendorStatusesStatus: HttpStatusCode.NotFound); | ||||
|  | ||||
|         var connector = provider.GetRequiredService<CertCcConnector>(); | ||||
|         await connector.FetchAsync(provider, CancellationToken.None); | ||||
|         await connector.ParseAsync(provider, CancellationToken.None); | ||||
|         await connector.MapAsync(provider, CancellationToken.None); | ||||
|  | ||||
|         var advisoryStore = provider.GetRequiredService<IAdvisoryStore>(); | ||||
|         var advisories = await advisoryStore.GetRecentAsync(10, CancellationToken.None); | ||||
|         advisories.Should().NotBeNull(); | ||||
|         advisories!.Should().Contain(advisory => advisory.AdvisoryKey == "certcc/vu-294418"); | ||||
|  | ||||
|         var documentStore = provider.GetRequiredService<IDocumentStore>(); | ||||
|         var vendorsDocument = await documentStore.FindBySourceAndUriAsync(CertCcConnectorPlugin.SourceName, VendorsUri.ToString(), CancellationToken.None); | ||||
|         vendorsDocument.Should().NotBeNull(); | ||||
|         vendorsDocument!.Status.Should().Be(DocumentStatuses.Mapped); | ||||
|         var vulsDocument = await documentStore.FindBySourceAndUriAsync(CertCcConnectorPlugin.SourceName, VulsUri.ToString(), CancellationToken.None); | ||||
|         vulsDocument.Should().BeNull(); | ||||
|         var noteDocument = await documentStore.FindBySourceAndUriAsync(CertCcConnectorPlugin.SourceName, NoteDetailUri.ToString(), CancellationToken.None); | ||||
|         noteDocument.Should().NotBeNull(); | ||||
|         noteDocument!.Status.Should().Be(DocumentStatuses.Mapped); | ||||
|  | ||||
|         var stateRepository = provider.GetRequiredService<ISourceStateRepository>(); | ||||
|         var state = await stateRepository.TryGetAsync(CertCcConnectorPlugin.SourceName, CancellationToken.None); | ||||
|         state.Should().NotBeNull(); | ||||
|         state!.Cursor.TryGetValue("pendingNotes", out var pendingNotesValue).Should().BeTrue(); | ||||
|         pendingNotesValue!.AsBsonArray.Should().BeEmpty(); | ||||
|         state.Cursor.TryGetValue("pendingDocuments", out var pendingDocsValue).Should().BeTrue(); | ||||
|         pendingDocsValue!.AsBsonArray.Should().BeEmpty(); | ||||
|         state.Cursor.TryGetValue("pendingMappings", out var pendingMappingsValue).Should().BeTrue(); | ||||
|         pendingMappingsValue!.AsBsonArray.Should().BeEmpty(); | ||||
|     } | ||||
|  | ||||
|     public Task InitializeAsync() => Task.CompletedTask; | ||||
|  | ||||
|     public async Task DisposeAsync() | ||||
| @@ -333,7 +373,9 @@ public sealed class CertCcConnectorTests : IAsyncLifetime | ||||
|         string vendorsEtag = "\"vendors-etag\"", | ||||
|         string vulsEtag = "\"vuls-etag\"", | ||||
|         string vendorStatusesEtag = "\"vendor-statuses-etag\"", | ||||
|         HttpStatusCode vendorsStatus = HttpStatusCode.OK) | ||||
|         HttpStatusCode vendorsStatus = HttpStatusCode.OK, | ||||
|         HttpStatusCode vulsStatus = HttpStatusCode.OK, | ||||
|         HttpStatusCode vendorStatusesStatus = HttpStatusCode.OK) | ||||
|     { | ||||
|         AddJsonResponse(NoteDetailUri, ReadFixture("vu-294418.json"), detailEtag); | ||||
|  | ||||
| @@ -354,8 +396,39 @@ public sealed class CertCcConnectorTests : IAsyncLifetime | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         AddJsonResponse(VulsUri, ReadFixture("vu-294418-vuls.json"), vulsEtag); | ||||
|         AddJsonResponse(VendorStatusesUri, ReadFixture("vendor-statuses-294418.json"), vendorStatusesEtag); | ||||
|         if (vulsStatus == HttpStatusCode.OK) | ||||
|         { | ||||
|             AddJsonResponse(VulsUri, ReadFixture("vu-294418-vuls.json"), vulsEtag); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             _handler.AddResponse(VulsUri, () => | ||||
|             { | ||||
|                 var response = new HttpResponseMessage(vulsStatus) | ||||
|                 { | ||||
|                     Content = new StringContent("vuls error", Encoding.UTF8, "text/plain"), | ||||
|                 }; | ||||
|                 response.Headers.ETag = new EntityTagHeaderValue(vulsEtag); | ||||
|                 return response; | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         if (vendorStatusesStatus == HttpStatusCode.OK) | ||||
|         { | ||||
|             AddJsonResponse(VendorStatusesUri, ReadFixture("vendor-statuses-294418.json"), vendorStatusesEtag); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             _handler.AddResponse(VendorStatusesUri, () => | ||||
|             { | ||||
|                 var response = new HttpResponseMessage(vendorStatusesStatus) | ||||
|                 { | ||||
|                     Content = new StringContent("vendor statuses error", Encoding.UTF8, "text/plain"), | ||||
|                 }; | ||||
|                 response.Headers.ETag = new EntityTagHeaderValue(vendorStatusesEtag); | ||||
|                 return response; | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void SeedDetailNotModifiedResponses(string detailEtag, string vendorsEtag, string vulsEtag, string vendorStatusesEtag) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user