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

@@ -0,0 +1,125 @@
using System;
using System.IO;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using StellaOps.Graph.Indexer.Ingestion.Sbom;
using Xunit;
namespace StellaOps.Graph.Indexer.Tests;
public sealed class SbomIngestServiceCollectionExtensionsTests : IDisposable
{
private static readonly string FixturesRoot =
Path.Combine(AppContext.BaseDirectory, "Fixtures", "v1");
private readonly string _tempDirectory;
public SbomIngestServiceCollectionExtensionsTests()
{
_tempDirectory = Path.Combine(Path.GetTempPath(), $"graph-indexer-{Guid.NewGuid():N}");
Directory.CreateDirectory(_tempDirectory);
}
[Fact]
public async Task AddSbomIngestPipeline_exports_snapshots_to_configured_directory()
{
var services = new ServiceCollection();
services.AddSingleton<IGraphDocumentWriter, CaptureWriter>();
services.AddSbomIngestPipeline(options => options.SnapshotRootDirectory = _tempDirectory);
using var provider = services.BuildServiceProvider();
var processor = provider.GetRequiredService<SbomIngestProcessor>();
var snapshot = LoadSnapshot();
await processor.ProcessAsync(snapshot, CancellationToken.None);
AssertSnapshotOutputs(_tempDirectory);
var writer = provider.GetRequiredService<IGraphDocumentWriter>() as CaptureWriter;
writer!.LastBatch.Should().NotBeNull();
}
[Fact]
public async Task AddSbomIngestPipeline_uses_environment_variable_when_not_configured()
{
var previous = Environment.GetEnvironmentVariable("STELLAOPS_GRAPH_SNAPSHOT_DIR");
try
{
Environment.SetEnvironmentVariable("STELLAOPS_GRAPH_SNAPSHOT_DIR", _tempDirectory);
var services = new ServiceCollection();
services.AddSingleton<IGraphDocumentWriter, CaptureWriter>();
services.AddSbomIngestPipeline();
using var provider = services.BuildServiceProvider();
var processor = provider.GetRequiredService<SbomIngestProcessor>();
var snapshot = LoadSnapshot();
await processor.ProcessAsync(snapshot, CancellationToken.None);
AssertSnapshotOutputs(_tempDirectory);
}
finally
{
Environment.SetEnvironmentVariable("STELLAOPS_GRAPH_SNAPSHOT_DIR", previous);
}
}
private static SbomSnapshot LoadSnapshot()
{
var path = Path.Combine(FixturesRoot, "sbom-snapshot.json");
var json = File.ReadAllText(path);
return JsonSerializer.Deserialize<SbomSnapshot>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
})!;
}
private static void AssertSnapshotOutputs(string root)
{
var manifestPath = Path.Combine(root, "manifest.json");
var adjacencyPath = Path.Combine(root, "adjacency.json");
var nodesPath = Path.Combine(root, "nodes.jsonl");
var edgesPath = Path.Combine(root, "edges.jsonl");
File.Exists(manifestPath).Should().BeTrue("manifest should be exported");
File.Exists(adjacencyPath).Should().BeTrue("adjacency manifest should be exported");
File.Exists(nodesPath).Should().BeTrue("node stream should be exported");
File.Exists(edgesPath).Should().BeTrue("edge stream should be exported");
new FileInfo(manifestPath).Length.Should().BeGreaterThan(0);
new FileInfo(adjacencyPath).Length.Should().BeGreaterThan(0);
new FileInfo(nodesPath).Length.Should().BeGreaterThan(0);
new FileInfo(edgesPath).Length.Should().BeGreaterThan(0);
}
public void Dispose()
{
try
{
if (Directory.Exists(_tempDirectory))
{
Directory.Delete(_tempDirectory, recursive: true);
}
}
catch
{
// Ignore cleanup failures in CI environments.
}
}
private sealed class CaptureWriter : IGraphDocumentWriter
{
public GraphBuildBatch? LastBatch { get; private set; }
public Task WriteAsync(GraphBuildBatch batch, CancellationToken cancellationToken)
{
LastBatch = batch;
return Task.CompletedTask;
}
}
}