Add tests for SBOM generation determinism across multiple formats

- Created `StellaOps.TestKit.Tests` project for unit tests related to determinism.
- Implemented `DeterminismManifestTests` to validate deterministic output for canonical bytes and strings, file read/write operations, and error handling for invalid schema versions.
- Added `SbomDeterminismTests` to ensure identical inputs produce consistent SBOMs across SPDX 3.0.1 and CycloneDX 1.6/1.7 formats, including parallel execution tests.
- Updated project references in `StellaOps.Integration.Determinism` to include the new determinism testing library.
This commit is contained in:
master
2025-12-23 18:56:12 +02:00
committed by StellaOps Bot
parent 7ac70ece71
commit 5590a99a1a
381 changed files with 21071 additions and 14678 deletions

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using StellaOps.Concelier.Connector.Common.Cursors;
using StellaOps.Concelier.Documents;
namespace StellaOps.Concelier.Connector.Ghsa.Internal;
@@ -36,15 +37,8 @@ internal sealed record GhsaCursor(
document["lastUpdatedExclusive"] = LastUpdatedExclusive.Value.UtcDateTime;
}
if (CurrentWindowStart.HasValue)
{
document["currentWindowStart"] = CurrentWindowStart.Value.UtcDateTime;
}
if (CurrentWindowEnd.HasValue)
{
document["currentWindowEnd"] = CurrentWindowEnd.Value.UtcDateTime;
}
new TimeWindowCursorState(CurrentWindowStart, CurrentWindowEnd)
.WriteTo(document, startField: "currentWindowStart", endField: "currentWindowEnd");
return document;
}
@@ -59,12 +53,7 @@ internal sealed record GhsaCursor(
var lastUpdatedExclusive = document.TryGetValue("lastUpdatedExclusive", out var lastUpdated)
? ParseDate(lastUpdated)
: null;
var windowStart = document.TryGetValue("currentWindowStart", out var windowStartValue)
? ParseDate(windowStartValue)
: null;
var windowEnd = document.TryGetValue("currentWindowEnd", out var windowEndValue)
? ParseDate(windowEndValue)
: null;
var window = TimeWindowCursorState.FromDocumentObject(document, startField: "currentWindowStart", endField: "currentWindowEnd");
var nextPage = document.TryGetValue("nextPage", out var nextPageValue) && nextPageValue.IsInt32
? Math.Max(1, nextPageValue.AsInt32)
: 1;
@@ -74,8 +63,8 @@ internal sealed record GhsaCursor(
return new GhsaCursor(
lastUpdatedExclusive,
windowStart,
windowEnd,
window.LastWindowStart,
window.LastWindowEnd,
nextPage,
pendingDocuments,
pendingMappings);

View File

@@ -157,6 +157,42 @@ public sealed class GhsaConnectorTests : IAsyncLifetime
Assert.Empty(pendingMappings.AsDocumentArray);
}
[Fact]
public async Task FetchAsync_ResumesFromPersistedCursorWindow()
{
var initialTime = new DateTimeOffset(2024, 10, 7, 0, 0, 0, TimeSpan.Zero);
await EnsureHarnessAsync(initialTime);
var harness = _harness!;
var since = initialTime - TimeSpan.FromDays(8);
var until = initialTime - TimeSpan.FromDays(7);
var stateRepository = harness.ServiceProvider.GetRequiredService<ISourceStateRepository>();
await stateRepository.UpdateCursorAsync(
GhsaConnectorPlugin.SourceName,
new DocumentObject
{
["currentWindowStart"] = since.UtcDateTime,
["currentWindowEnd"] = until.UtcDateTime,
["nextPage"] = 2,
["pendingDocuments"] = new DocumentArray(),
["pendingMappings"] = new DocumentArray(),
},
initialTime,
CancellationToken.None);
var listUri = new Uri($"https://ghsa.test/security/advisories?updated_since={Uri.EscapeDataString(since.ToString("O"))}&updated_until={Uri.EscapeDataString(until.ToString("O"))}&page=2&per_page=5");
harness.Handler.AddJsonResponse(listUri, """{"advisories":[],"pagination":{"page":2,"has_next_page":false}}""");
harness.Handler.SetFallback(_ => new HttpResponseMessage(HttpStatusCode.NotFound));
var connector = new GhsaConnectorPlugin().Create(harness.ServiceProvider);
await connector.FetchAsync(harness.ServiceProvider, CancellationToken.None);
var request = Assert.Single(harness.Handler.Requests);
Assert.Equal(listUri, request.Uri);
harness.Handler.AssertNoPendingResponses();
}
private async Task EnsureHarnessAsync(DateTimeOffset initialTime)
{
if (_harness is not null)