Files
git.stella-ops.org/src/__Tests/unit/StellaOps.AuditPack.Tests/AuditPackExportServiceTests.cs

231 lines
7.0 KiB
C#

// -----------------------------------------------------------------------------
// AuditPackExportServiceTests.cs
// Sprint: SPRINT_1227_0005_0003_FE_copy_audit_export
// Task: T10 — Unit tests for AuditPackExportService
// -----------------------------------------------------------------------------
namespace StellaOps.AuditPack.Tests;
using StellaOps.AuditPack.Models;
using StellaOps.AuditPack.Services;
using System.IO.Compression;
using System.Text.Json;
[Trait("Category", "Unit")]
public class AuditPackExportServiceTests
{
private readonly AuditPackExportService _service;
public AuditPackExportServiceTests()
{
_service = new AuditPackExportService(
new MockAuditBundleWriter(),
null);
}
[Fact]
public async Task ExportAsync_Zip_CreatesValidZipArchive()
{
// Arrange
var request = new ExportRequest
{
ScanId = "scan-123",
Format = ExportFormat.Zip,
Segments = [ExportSegment.Sbom, ExportSegment.Match],
IncludeAttestations = true,
IncludeProofChain = false,
Filename = "test-export"
};
// Act
var result = await _service.ExportAsync(request);
// Assert
result.Success.Should().BeTrue();
result.ContentType.Should().Be("application/zip");
result.Filename.Should().Be("test-export.zip");
result.Data.Should().NotBeNull();
result.SizeBytes.Should().BeGreaterThan(0);
// Verify ZIP structure
using var memoryStream = new MemoryStream(result.Data!);
using var archive = new ZipArchive(memoryStream, ZipArchiveMode.Read);
archive.Entries.Should().Contain(e => e.FullName == "manifest.json");
}
[Fact]
public async Task ExportAsync_Json_CreatesSingleJsonFile()
{
// Arrange
var request = new ExportRequest
{
ScanId = "scan-456",
Format = ExportFormat.Json,
Segments = [ExportSegment.Sbom],
IncludeAttestations = false,
IncludeProofChain = false,
Filename = "test-json"
};
// Act
var result = await _service.ExportAsync(request);
// Assert
result.Success.Should().BeTrue();
result.ContentType.Should().Be("application/json");
result.Filename.Should().Be("test-json.json");
// Verify JSON structure
using var jsonDoc = JsonDocument.Parse(result.Data!);
var root = jsonDoc.RootElement;
root.TryGetProperty("scanId", out var scanIdProp).Should().BeTrue();
scanIdProp.GetString().Should().Be("scan-456");
root.TryGetProperty("segments", out _).Should().BeTrue();
}
[Fact]
public async Task ExportAsync_Dsse_CreatesDsseEnvelope()
{
// Arrange
var request = new ExportRequest
{
ScanId = "scan-789",
Format = ExportFormat.Dsse,
Segments = [ExportSegment.Policy],
IncludeAttestations = true,
IncludeProofChain = true,
Filename = "test-dsse"
};
// Act
var result = await _service.ExportAsync(request);
// Assert
result.Success.Should().BeTrue();
result.ContentType.Should().Be("application/vnd.dsse+json");
result.Filename.Should().Be("test-dsse.dsse.json");
// Verify DSSE structure
using var jsonDoc = JsonDocument.Parse(result.Data!);
var root = jsonDoc.RootElement;
root.TryGetProperty("payloadType", out var payloadType).Should().BeTrue();
payloadType.GetString().Should().Be("application/vnd.stellaops.audit-pack+json");
root.TryGetProperty("payload", out _).Should().BeTrue();
root.TryGetProperty("signatures", out _).Should().BeTrue();
}
[Fact]
public async Task ExportAsync_AllSegments_IncludesAllInZip()
{
// Arrange
var allSegments = new[]
{
ExportSegment.Sbom,
ExportSegment.Match,
ExportSegment.Reachability,
ExportSegment.Guards,
ExportSegment.Runtime,
ExportSegment.Policy
};
var request = new ExportRequest
{
ScanId = "scan-full",
Format = ExportFormat.Zip,
Segments = allSegments,
IncludeAttestations = true,
IncludeProofChain = true,
Filename = "full-export"
};
// Act
var result = await _service.ExportAsync(request);
// Assert
result.Success.Should().BeTrue();
using var memoryStream = new MemoryStream(result.Data!);
using var archive = new ZipArchive(memoryStream, ZipArchiveMode.Read);
// Should have manifest + 6 segments + attestations + proof chain
archive.Entries.Count.Should().BeGreaterThanOrEqualTo(3);
}
[Fact]
public async Task ExportAsync_EmptySegments_StillCreatesValidExport()
{
// Arrange
var request = new ExportRequest
{
ScanId = "scan-empty",
Format = ExportFormat.Json,
Segments = [],
IncludeAttestations = false,
IncludeProofChain = false,
Filename = "empty-export"
};
// Act
var result = await _service.ExportAsync(request);
// Assert
result.Success.Should().BeTrue();
result.Data.Should().NotBeNull();
}
[Fact]
public async Task ExportAsync_UnsupportedFormat_ReturnsFailed()
{
// Arrange
var request = new ExportRequest
{
ScanId = "scan-fail",
Format = (ExportFormat)999, // Invalid format
Segments = [ExportSegment.Sbom],
IncludeAttestations = false,
IncludeProofChain = false,
Filename = "fail-export"
};
// Act
var result = await _service.ExportAsync(request);
// Assert
result.Success.Should().BeFalse();
result.Error.Should().Contain("Unsupported");
}
[Fact]
public async Task ExportAsync_CancellationRequested_ThrowsOperationCanceled()
{
// Arrange
var request = new ExportRequest
{
ScanId = "scan-cancel",
Format = ExportFormat.Zip,
Segments = [ExportSegment.Sbom],
IncludeAttestations = false,
IncludeProofChain = false,
Filename = "cancel-export"
};
var cts = new CancellationTokenSource();
cts.Cancel();
// Act & Assert
await Assert.ThrowsAsync<OperationCanceledException>(
() => _service.ExportAsync(request, cts.Token));
}
// Mock implementation for testing
private class MockAuditBundleWriter : IAuditBundleWriter
{
public Task<AuditBundleWriteResult> WriteAsync(
AuditBundleWriteRequest request,
CancellationToken cancellationToken = default)
{
return Task.FromResult(new AuditBundleWriteResult { Success = true });
}
}
}