using System; using System.Linq; using StellaOps.Concelier.Merge.Identity; using StellaOps.Concelier.Models; using Xunit; namespace StellaOps.Concelier.Merge.Tests; public sealed class AdvisoryIdentityResolverTests { private readonly AdvisoryIdentityResolver _resolver = new(); [Fact] public void Resolve_GroupsBySharedCveAlias() { var nvd = CreateAdvisory("CVE-2025-1234", aliases: new[] { "CVE-2025-1234" }, source: "nvd"); var vendor = CreateAdvisory("VSA-2025-01", aliases: new[] { "CVE-2025-1234", "VSA-2025-01" }, source: "vendor"); var clusters = _resolver.Resolve(new[] { nvd, vendor }); var cluster = Assert.Single(clusters); Assert.Equal("CVE-2025-1234", cluster.AdvisoryKey); Assert.Equal(2, cluster.Advisories.Length); Assert.True(cluster.Aliases.Any(alias => alias.Value == "CVE-2025-1234")); } [Fact] public void Resolve_PrefersPsirtAliasWhenNoCve() { var vendor = CreateAdvisory("VMSA-2025-0001", aliases: new[] { "VMSA-2025-0001" }, source: "vmware"); var osv = CreateAdvisory("OSV-2025-1", aliases: new[] { "OSV-2025-1", "GHSA-xxxx-yyyy-zzzz", "VMSA-2025-0001" }, source: "osv"); var clusters = _resolver.Resolve(new[] { vendor, osv }); var cluster = Assert.Single(clusters); Assert.Equal("VMSA-2025-0001", cluster.AdvisoryKey); Assert.Equal(2, cluster.Advisories.Length); Assert.True(cluster.Aliases.Any(alias => alias.Value == "VMSA-2025-0001")); } [Fact] public void Resolve_FallsBackToGhsaWhenOnlyGhsaPresent() { var ghsa = CreateAdvisory("GHSA-aaaa-bbbb-cccc", aliases: new[] { "GHSA-aaaa-bbbb-cccc" }, source: "ghsa"); var osv = CreateAdvisory("OSV-2025-99", aliases: new[] { "OSV-2025-99", "GHSA-aaaa-bbbb-cccc" }, source: "osv"); var clusters = _resolver.Resolve(new[] { ghsa, osv }); var cluster = Assert.Single(clusters); Assert.Equal("GHSA-aaaa-bbbb-cccc", cluster.AdvisoryKey); Assert.Equal(2, cluster.Advisories.Length); Assert.True(cluster.Aliases.Any(alias => alias.Value == "GHSA-aaaa-bbbb-cccc")); } [Fact] public void Resolve_GroupsByKeyWhenNoAliases() { var first = CreateAdvisory("custom-1", aliases: Array.Empty(), source: "source-a"); var second = CreateAdvisory("custom-1", aliases: Array.Empty(), source: "source-b"); var clusters = _resolver.Resolve(new[] { first, second }); var cluster = Assert.Single(clusters); Assert.Equal("custom-1", cluster.AdvisoryKey); Assert.Equal(2, cluster.Advisories.Length); Assert.Contains(cluster.Aliases, alias => alias.Value == "custom-1"); } private static Advisory CreateAdvisory(string key, string[] aliases, string source) { var provenance = new[] { new AdvisoryProvenance(source, "mapping", key, DateTimeOffset.UtcNow), }; return new Advisory( key, $"{key} title", $"{key} summary", "en", DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, null, exploitKnown: false, aliases, Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), provenance); } }