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.
128 lines
5.4 KiB
C#
128 lines
5.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using StellaOps.EvidenceLocker.Core.Builders;
|
|
using StellaOps.EvidenceLocker.Core.Domain;
|
|
using StellaOps.EvidenceLocker.Core.Repositories;
|
|
using StellaOps.EvidenceLocker.Infrastructure.Builders;
|
|
using Xunit;
|
|
|
|
namespace StellaOps.EvidenceLocker.Tests;
|
|
|
|
public sealed class EvidenceBundleBuilderTests
|
|
{
|
|
private readonly FakeRepository _repository = new();
|
|
private readonly IEvidenceBundleBuilder _builder;
|
|
|
|
public EvidenceBundleBuilderTests()
|
|
{
|
|
_builder = new EvidenceBundleBuilder(_repository, new MerkleTreeCalculator());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task BuildAsync_ComputesDeterministicRootAndPersists()
|
|
{
|
|
var bundleId = EvidenceBundleId.FromGuid(Guid.NewGuid());
|
|
var tenantId = TenantId.FromGuid(Guid.NewGuid());
|
|
var request = new EvidenceBundleBuildRequest(
|
|
bundleId,
|
|
tenantId,
|
|
EvidenceBundleKind.Job,
|
|
DateTimeOffset.Parse("2025-11-03T15:04:05Z"),
|
|
new Dictionary<string, string> { ["run-id"] = "job-42" },
|
|
new List<EvidenceBundleMaterial>
|
|
{
|
|
new("inputs", "config/env.json", "5a6b7c", 1024, "application/json"),
|
|
new("outputs", "reports/result.txt", "7f8e9d", 2048, "text/plain")
|
|
});
|
|
|
|
var result = await _builder.BuildAsync(request, CancellationToken.None);
|
|
|
|
Assert.Equal(EvidenceBundleStatus.Sealed, _repository.LastStatus);
|
|
Assert.Equal(bundleId, _repository.LastBundleId);
|
|
Assert.Equal(tenantId, _repository.LastTenantId);
|
|
Assert.Equal(DateTimeOffset.Parse("2025-11-03T15:04:05Z"), _repository.LastUpdatedAt);
|
|
|
|
Assert.Equal(result.RootHash, _repository.LastRootHash);
|
|
Assert.Equal(2, result.Manifest.Entries.Count);
|
|
Assert.True(result.Manifest.Entries.SequenceEqual(
|
|
result.Manifest.Entries.OrderBy(entry => entry.CanonicalPath, StringComparer.Ordinal)));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task BuildAsync_NormalizesSectionAndPath()
|
|
{
|
|
var request = new EvidenceBundleBuildRequest(
|
|
EvidenceBundleId.FromGuid(Guid.NewGuid()),
|
|
TenantId.FromGuid(Guid.NewGuid()),
|
|
EvidenceBundleKind.Evaluation,
|
|
DateTimeOffset.UtcNow,
|
|
new Dictionary<string, string>(),
|
|
new List<EvidenceBundleMaterial>
|
|
{
|
|
new(" Inputs ", "./Config/Env.JSON ", "abc123", 10, "application/json"),
|
|
new("OUTPUTS", "\\Logs\\app.log", "def456", 20, "text/plain")
|
|
});
|
|
|
|
var result = await _builder.BuildAsync(request, CancellationToken.None);
|
|
|
|
Assert.Collection(result.Manifest.Entries,
|
|
entry => Assert.Equal("inputs/config/env.json", entry.CanonicalPath),
|
|
entry => Assert.Equal("outputs/logs/app.log", entry.CanonicalPath));
|
|
}
|
|
|
|
private sealed class FakeRepository : IEvidenceBundleRepository
|
|
{
|
|
public EvidenceBundleId LastBundleId { get; private set; }
|
|
public TenantId LastTenantId { get; private set; }
|
|
public EvidenceBundleStatus LastStatus { get; private set; }
|
|
public string? LastRootHash { get; private set; }
|
|
public DateTimeOffset LastUpdatedAt { get; private set; }
|
|
|
|
public Task CreateBundleAsync(EvidenceBundle bundle, CancellationToken cancellationToken)
|
|
=> Task.CompletedTask;
|
|
|
|
public Task SetBundleAssemblyAsync(
|
|
EvidenceBundleId bundleId,
|
|
TenantId tenantId,
|
|
EvidenceBundleStatus status,
|
|
string rootHash,
|
|
DateTimeOffset updatedAt,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
LastBundleId = bundleId;
|
|
LastTenantId = tenantId;
|
|
LastStatus = status;
|
|
LastRootHash = rootHash;
|
|
LastUpdatedAt = updatedAt;
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task MarkBundleSealedAsync(EvidenceBundleId bundleId, TenantId tenantId, EvidenceBundleStatus status, DateTimeOffset sealedAt, CancellationToken cancellationToken)
|
|
=> Task.CompletedTask;
|
|
|
|
public Task UpsertSignatureAsync(EvidenceBundleSignature signature, CancellationToken cancellationToken)
|
|
=> Task.CompletedTask;
|
|
|
|
public Task<EvidenceBundleDetails?> GetBundleAsync(EvidenceBundleId bundleId, TenantId tenantId, CancellationToken cancellationToken)
|
|
=> Task.FromResult<EvidenceBundleDetails?>(null);
|
|
|
|
public Task<bool> ExistsAsync(EvidenceBundleId bundleId, TenantId tenantId, CancellationToken cancellationToken)
|
|
=> Task.FromResult(true);
|
|
|
|
public Task<EvidenceHold> CreateHoldAsync(EvidenceHold hold, CancellationToken cancellationToken)
|
|
=> Task.FromResult(hold);
|
|
|
|
public Task ExtendBundleRetentionAsync(EvidenceBundleId bundleId, TenantId tenantId, DateTimeOffset? holdExpiresAt, DateTimeOffset processedAt, CancellationToken cancellationToken)
|
|
=> Task.CompletedTask;
|
|
|
|
public Task UpdateStorageKeyAsync(EvidenceBundleId bundleId, TenantId tenantId, string storageKey, CancellationToken cancellationToken)
|
|
=> Task.CompletedTask;
|
|
|
|
public Task UpdatePortableStorageKeyAsync(EvidenceBundleId bundleId, TenantId tenantId, string storageKey, DateTimeOffset generatedAt, CancellationToken cancellationToken)
|
|
=> Task.CompletedTask;
|
|
}
|
|
}
|