using System; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; using System.Text.Json; using FluentAssertions; using Json.Schema; using StellaOps.Scanner.Core.Contracts; using StellaOps.Scanner.Emit.Composition; using Xunit; namespace StellaOps.Scanner.Emit.Tests.Composition; public sealed class CycloneDxSchemaValidationTests { [Fact] public void Compose_InventoryPassesCycloneDx17Schema() { var request = BuildRequest(); var composer = new CycloneDxComposer(); var result = composer.Compose(request); using var document = JsonDocument.Parse(result.Inventory.JsonBytes); var schema = LoadSchema(); var validation = schema.Evaluate(document.RootElement); validation.IsValid.Should().BeTrue(validation.ToString()); } private static JsonSchema LoadSchema() { var schemaPath = Path.Combine( AppContext.BaseDirectory, "Fixtures", "schemas", "cyclonedx-bom-1.7.schema.json"); var schemaJson = File.ReadAllText(schemaPath); return JsonSchema.FromText(schemaJson); } private static SbomCompositionRequest BuildRequest() { var fragments = new[] { LayerComponentFragment.Create("sha256:layer1", new[] { new ComponentRecord { Identity = ComponentIdentity.Create( "pkg:npm/demo", "demo", "1.0.0", "pkg:npm/demo@1.0.0", "library"), LayerDigest = "sha256:layer1", Evidence = ImmutableArray.Create(ComponentEvidence.FromPath("/app/node_modules/demo/package.json")), Usage = ComponentUsage.Create(false), Metadata = new ComponentMetadata { Properties = new Dictionary { ["stellaops:source"] = "package-lock.json", }, }, } }) }; var image = new ImageArtifactDescriptor { ImageDigest = "sha256:abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd", ImageReference = "registry.example.com/demo/app:1.0.0", Repository = "registry.example.com/demo/app", Tag = "1.0.0", Architecture = "amd64", }; return SbomCompositionRequest.Create( image, fragments, new DateTimeOffset(2025, 10, 20, 0, 0, 0, TimeSpan.Zero), generatorName: "StellaOps.Scanner", generatorVersion: "0.10.0"); } }