using FluentAssertions; using StellaOps.AirGap.Importer.Reconciliation; namespace StellaOps.AirGap.Importer.Tests.Reconciliation; public sealed class ArtifactIndexTests { [Fact] public void NormalizeDigest_BareHex_AddsPrefixAndLowercases() { var hex = new string('A', 64); ArtifactIndex.NormalizeDigest(hex).Should().Be("sha256:" + new string('a', 64)); } [Fact] public void NormalizeDigest_WithSha256Prefix_IsCanonical() { var hex = new string('B', 64); ArtifactIndex.NormalizeDigest("sha256:" + hex).Should().Be("sha256:" + new string('b', 64)); } [Fact] public void NormalizeDigest_WithOtherAlgorithm_Throws() { var ex = Assert.Throws(() => ArtifactIndex.NormalizeDigest("sha512:" + new string('a', 64))); ex.Message.Should().Contain("Only sha256"); } [Fact] public void AddOrUpdate_MergesEntries_DeduplicatesAndSorts() { var digest = new string('c', 64); var entryA = ArtifactEntry.Empty(digest) with { Sboms = new[] { new SbomReference("b", "b.json", SbomFormat.CycloneDx, null), new SbomReference("a", "a.json", SbomFormat.Spdx, null), } }; var entryB = ArtifactEntry.Empty("sha256:" + digest.ToUpperInvariant()) with { Sboms = new[] { new SbomReference("a", "a2.json", SbomFormat.CycloneDx, null), new SbomReference("c", "c.json", SbomFormat.Spdx, null), } }; var index = new ArtifactIndex(); index.AddOrUpdate(entryA); index.AddOrUpdate(entryB); var stored = index.Get("sha256:" + digest); stored.Should().NotBeNull(); stored!.Digest.Should().Be("sha256:" + digest); stored.Sboms.Select(s => (s.ContentHash, s.FilePath)).Should().Equal( ("a", "a.json"), ("b", "b.json"), ("c", "c.json")); } }