Add unit tests for SBOM ingestion and transformation
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Implement `SbomIngestServiceCollectionExtensionsTests` to verify the SBOM ingestion pipeline exports snapshots correctly.
- Create `SbomIngestTransformerTests` to ensure the transformation produces expected nodes and edges, including deduplication of license nodes and normalization of timestamps.
- Add `SbomSnapshotExporterTests` to test the export functionality for manifest, adjacency, nodes, and edges.
- Introduce `VexOverlayTransformerTests` to validate the transformation of VEX nodes and edges.
- Set up project file for the test project with necessary dependencies and configurations.
- Include JSON fixture files for testing purposes.
This commit is contained in:
master
2025-11-04 07:49:39 +02:00
parent f72c5c513a
commit 2eb6852d34
491 changed files with 39445 additions and 3917 deletions

View File

@@ -39,6 +39,9 @@ internal sealed class IssuerDirectoryClient : IIssuerDirectoryClient
ArgumentException.ThrowIfNullOrWhiteSpace(tenantId);
ArgumentException.ThrowIfNullOrWhiteSpace(issuerId);
tenantId = tenantId.Trim();
issuerId = issuerId.Trim();
var cacheKey = CacheKey("keys", tenantId, issuerId, includeGlobal.ToString(CultureInfo.InvariantCulture));
if (_cache.TryGetValue(cacheKey, out IReadOnlyList<IssuerKeyModel>? cached) && cached is not null)
{
@@ -77,6 +80,9 @@ internal sealed class IssuerDirectoryClient : IIssuerDirectoryClient
ArgumentException.ThrowIfNullOrWhiteSpace(tenantId);
ArgumentException.ThrowIfNullOrWhiteSpace(issuerId);
tenantId = tenantId.Trim();
issuerId = issuerId.Trim();
var cacheKey = CacheKey("trust", tenantId, issuerId, includeGlobal.ToString(CultureInfo.InvariantCulture));
if (_cache.TryGetValue(cacheKey, out IssuerTrustResponseModel? cached) && cached is not null)
{
@@ -105,6 +111,84 @@ internal sealed class IssuerDirectoryClient : IIssuerDirectoryClient
return payload;
}
public async ValueTask<IssuerTrustResponseModel> SetIssuerTrustAsync(
string tenantId,
string issuerId,
decimal weight,
string? reason,
CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrWhiteSpace(tenantId);
ArgumentException.ThrowIfNullOrWhiteSpace(issuerId);
var normalizedTenant = tenantId.Trim();
var normalizedReason = string.IsNullOrWhiteSpace(reason) ? null : reason.Trim();
var requestUri = $"issuer-directory/issuers/{Uri.EscapeDataString(issuerId)}/trust";
using var request = new HttpRequestMessage(HttpMethod.Put, requestUri)
{
Content = JsonContent.Create(new IssuerTrustSetRequestModel(weight, normalizedReason))
};
request.Headers.TryAddWithoutValidation(_options.TenantHeader, normalizedTenant);
if (!string.IsNullOrWhiteSpace(normalizedReason))
{
request.Headers.TryAddWithoutValidation(_options.AuditReasonHeader, normalizedReason);
}
using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
_logger.LogWarning(
"Issuer Directory trust update failed for {IssuerId} (tenant={TenantId}) {StatusCode}",
issuerId,
normalizedTenant,
response.StatusCode);
response.EnsureSuccessStatusCode();
}
InvalidateTrustCache(normalizedTenant, issuerId);
var payload = await response.Content.ReadFromJsonAsync<IssuerTrustResponseModel>(cancellationToken: cancellationToken)
.ConfigureAwait(false) ?? new IssuerTrustResponseModel(null, null, 0m);
return payload;
}
public async ValueTask DeleteIssuerTrustAsync(
string tenantId,
string issuerId,
string? reason,
CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrWhiteSpace(tenantId);
ArgumentException.ThrowIfNullOrWhiteSpace(issuerId);
var normalizedTenant = tenantId.Trim();
var normalizedReason = string.IsNullOrWhiteSpace(reason) ? null : reason.Trim();
var requestUri = $"issuer-directory/issuers/{Uri.EscapeDataString(issuerId)}/trust";
using var request = new HttpRequestMessage(HttpMethod.Delete, requestUri);
request.Headers.TryAddWithoutValidation(_options.TenantHeader, normalizedTenant);
if (!string.IsNullOrWhiteSpace(normalizedReason))
{
request.Headers.TryAddWithoutValidation(_options.AuditReasonHeader, normalizedReason);
}
using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
_logger.LogWarning(
"Issuer Directory trust delete failed for {IssuerId} (tenant={TenantId}) {StatusCode}",
issuerId,
normalizedTenant,
response.StatusCode);
response.EnsureSuccessStatusCode();
}
InvalidateTrustCache(normalizedTenant, issuerId);
}
private static string CacheKey(string prefix, params string[] parts)
{
if (parts is null || parts.Length == 0)
@@ -117,4 +201,11 @@ internal sealed class IssuerDirectoryClient : IIssuerDirectoryClient
Array.Copy(parts, 0, segments, 1, parts.Length);
return string.Join('|', segments);
}
private void InvalidateTrustCache(string tenantId, string issuerId)
{
_cache.Remove(CacheKey("trust", tenantId, issuerId, bool.FalseString));
_cache.Remove(CacheKey("trust", tenantId, issuerId, bool.TrueString));
}
}