// ----------------------------------------------------------------------------- // SourcePrecedenceLatticeTests.cs // Sprint: SPRINT_8200_0015_0001_CONCEL_backport_integration // Task: BACKPORT-8200-022 // Description: Unit tests for ConfigurableSourcePrecedenceLattice // ----------------------------------------------------------------------------- using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using StellaOps.Concelier.Merge.Backport; using StellaOps.Concelier.Merge.Precedence; namespace StellaOps.Concelier.Merge.Tests.Precedence; public sealed class SourcePrecedenceLatticeTests { private readonly TestLogger _logger = new(); [Theory] [InlineData("vendor-psirt", 10)] [InlineData("cisco", 10)] [InlineData("oracle", 10)] [InlineData("microsoft", 10)] [InlineData("debian", 20)] [InlineData("redhat", 20)] [InlineData("ubuntu", 20)] [InlineData("nvd", 40)] [InlineData("ghsa", 35)] [InlineData("osv", 30)] [InlineData("community", 100)] public void GetPrecedence_ReturnsDefaultPrecedence_ForKnownSources(string source, int expected) { var lattice = CreateLattice(); var precedence = lattice.GetPrecedence(source); Assert.Equal(expected, precedence); } [Fact] public void GetPrecedence_ReturnsHighValue_ForUnknownSource() { var lattice = CreateLattice(); var precedence = lattice.GetPrecedence("unknown-source"); Assert.Equal(1000, precedence); } [Theory] [InlineData("DEBIAN", 20)] [InlineData("Debian", 20)] [InlineData("dEbIaN", 20)] public void GetPrecedence_IsCaseInsensitive(string source, int expected) { var lattice = CreateLattice(); var precedence = lattice.GetPrecedence(source); Assert.Equal(expected, precedence); } [Fact] public void Compare_VendorTakesHigherPrecedence_OverDistro() { var lattice = CreateLattice(); var result = lattice.Compare("vendor-psirt", "debian"); Assert.Equal(SourceComparison.Source1Higher, result); } [Fact] public void Compare_DistroTakesHigherPrecedence_OverNvd() { var lattice = CreateLattice(); var result = lattice.Compare("debian", "nvd"); Assert.Equal(SourceComparison.Source1Higher, result); } [Fact] public void Compare_SameDistros_AreEqual() { var lattice = CreateLattice(); var result = lattice.Compare("debian", "redhat"); Assert.Equal(SourceComparison.Equal, result); } [Theory] [InlineData("debian", true)] [InlineData("redhat", true)] [InlineData("suse", true)] [InlineData("ubuntu", true)] [InlineData("alpine", true)] [InlineData("astra", true)] [InlineData("centos", true)] [InlineData("fedora", true)] [InlineData("rocky", true)] [InlineData("alma", true)] [InlineData("nvd", false)] [InlineData("ghsa", false)] [InlineData("vendor-psirt", false)] [InlineData("unknown", false)] public void IsDistroSource_CorrectlyIdentifiesSources(string source, bool expected) { var lattice = CreateLattice(); var result = lattice.IsDistroSource(source); Assert.Equal(expected, result); } [Fact] public void BackportBoostAmount_ReturnsDefaultValue() { var lattice = CreateLattice(); Assert.Equal(15, lattice.BackportBoostAmount); } [Fact] public void BackportBoostThreshold_ReturnsDefaultValue() { var lattice = CreateLattice(); Assert.Equal(0.7, lattice.BackportBoostThreshold); } [Fact] public void GetPrecedence_AppliesBackportBoost_WhenDistroHasHighConfidenceEvidence() { var lattice = CreateLattice(); var context = new BackportContext { CveId = "CVE-2024-1234", HasBackportEvidence = true, EvidenceConfidence = 0.9, EvidenceTier = BackportEvidenceTier.DistroAdvisory }; var basePrecedence = lattice.GetPrecedence("debian"); var boostedPrecedence = lattice.GetPrecedence("debian", context); Assert.Equal(20, basePrecedence); Assert.Equal(5, boostedPrecedence); // 20 - 15 = 5 } [Fact] public void GetPrecedence_DoesNotApplyBackportBoost_WhenConfidenceBelowThreshold() { var lattice = CreateLattice(); var context = new BackportContext { CveId = "CVE-2024-1234", HasBackportEvidence = true, EvidenceConfidence = 0.5, // Below 0.7 threshold EvidenceTier = BackportEvidenceTier.ChangelogMention }; var precedence = lattice.GetPrecedence("debian", context); Assert.Equal(20, precedence); // No boost applied } [Fact] public void GetPrecedence_DoesNotApplyBackportBoost_WhenNoEvidence() { var lattice = CreateLattice(); var context = new BackportContext { CveId = "CVE-2024-1234", HasBackportEvidence = false, EvidenceConfidence = 0.9 }; var precedence = lattice.GetPrecedence("debian", context); Assert.Equal(20, precedence); // No boost applied } [Fact] public void GetPrecedence_DoesNotApplyBackportBoost_ToNonDistroSources() { var lattice = CreateLattice(); var context = new BackportContext { CveId = "CVE-2024-1234", HasBackportEvidence = true, EvidenceConfidence = 0.9, EvidenceTier = BackportEvidenceTier.DistroAdvisory }; var precedence = lattice.GetPrecedence("nvd", context); Assert.Equal(40, precedence); // No boost - not a distro source } [Fact] public void GetPrecedence_LowerTierEvidence_RequiresHigherConfidence() { var lattice = CreateLattice(); // Tier 3 (PatchHeader) with 80% confidence - should not get boost var lowConfidenceContext = new BackportContext { CveId = "CVE-2024-1234", HasBackportEvidence = true, EvidenceConfidence = 0.8, EvidenceTier = BackportEvidenceTier.PatchHeader }; // Tier 3 with 95% confidence - should get boost var highConfidenceContext = new BackportContext { CveId = "CVE-2024-1234", HasBackportEvidence = true, EvidenceConfidence = 0.95, EvidenceTier = BackportEvidenceTier.PatchHeader }; var noBoost = lattice.GetPrecedence("debian", lowConfidenceContext); var withBoost = lattice.GetPrecedence("debian", highConfidenceContext); Assert.Equal(20, noBoost); // No boost - 80% < 90% required for tier 3 Assert.Equal(5, withBoost); // Boost applied - 95% >= 90% } [Fact] public void Compare_DistroWithBackportBoost_TakesHigherPrecedence_ThanVendor() { var lattice = CreateLattice(); var context = new BackportContext { CveId = "CVE-2024-1234", HasBackportEvidence = true, EvidenceConfidence = 0.95, EvidenceTier = BackportEvidenceTier.DistroAdvisory }; // Without context, vendor-psirt (10) > debian (20) var withoutContext = lattice.Compare("debian", "vendor-psirt"); Assert.Equal(SourceComparison.Source2Higher, withoutContext); // With backport context, debian (20 - 15 = 5) > vendor-psirt (10) var withContext = lattice.Compare("debian", "vendor-psirt", context); Assert.Equal(SourceComparison.Source1Higher, withContext); } [Fact] public void GetPrecedence_UsesCveSpecificOverride_WhenConfigured() { var config = new PrecedenceConfig { Overrides = new(StringComparer.OrdinalIgnoreCase) { ["CVE-2024-9999:debian"] = 5 } }; var lattice = CreateLattice(config); var context = new BackportContext { CveId = "CVE-2024-9999", HasBackportEvidence = false }; var precedence = lattice.GetPrecedence("debian", context); Assert.Equal(5, precedence); // Uses override, not default } [Fact] public void GetPrecedence_CveOverride_TakesPrecedence_OverBackportBoost() { var config = new PrecedenceConfig { Overrides = new(StringComparer.OrdinalIgnoreCase) { ["CVE-2024-9999:debian"] = 50 // Explicitly set lower precedence } }; var lattice = CreateLattice(config); var context = new BackportContext { CveId = "CVE-2024-9999", HasBackportEvidence = true, EvidenceConfidence = 0.95, EvidenceTier = BackportEvidenceTier.DistroAdvisory }; var precedence = lattice.GetPrecedence("debian", context); // Override takes precedence, boost not applied Assert.Equal(50, precedence); } [Fact] public void GetPrecedence_WithBackportBoostDisabled_DoesNotApplyBoost() { var config = new PrecedenceConfig { EnableBackportBoost = false }; var lattice = CreateLattice(config); var context = new BackportContext { CveId = "CVE-2024-1234", HasBackportEvidence = true, EvidenceConfidence = 0.95, EvidenceTier = BackportEvidenceTier.DistroAdvisory }; var precedence = lattice.GetPrecedence("debian", context); Assert.Equal(20, precedence); // No boost - disabled in config } [Theory] [InlineData("")] [InlineData(" ")] public void GetPrecedence_ThrowsOnInvalidSource(string source) { var lattice = CreateLattice(); Assert.Throws(() => lattice.GetPrecedence(source)); } private ConfigurableSourcePrecedenceLattice CreateLattice(PrecedenceConfig? config = null) { var options = Microsoft.Extensions.Options.Options.Create(config ?? new PrecedenceConfig()); return new ConfigurableSourcePrecedenceLattice(options, _logger); } } public sealed class PrecedenceExceptionRuleTests { [Theory] [InlineData("CVE-2024-1234", "CVE-2024-1234", true)] [InlineData("CVE-2024-1234", "CVE-2024-1235", false)] [InlineData("CVE-2024-*", "CVE-2024-1234", true)] [InlineData("CVE-2024-*", "CVE-2024-9999", true)] [InlineData("CVE-2024-*", "CVE-2025-1234", false)] [InlineData("CVE-*", "CVE-2024-1234", true)] public void Matches_WorksWithPatterns(string pattern, string cveId, bool expected) { var rule = new PrecedenceExceptionRule { CvePattern = pattern, Source = "debian", Precedence = 5 }; var result = rule.Matches(cveId); Assert.Equal(expected, result); } [Theory] [InlineData("")] [InlineData(null)] [InlineData(" ")] public void Matches_ReturnsFalse_ForInvalidCveId(string? cveId) { var rule = new PrecedenceExceptionRule { CvePattern = "CVE-2024-*", Source = "debian", Precedence = 5 }; var result = rule.Matches(cveId!); Assert.False(result); } } public sealed class ExtendedPrecedenceConfigTests { [Fact] public void GetActiveRules_ReturnsOnlyActiveRules() { var config = new ExtendedPrecedenceConfig { ExceptionRules = [ new PrecedenceExceptionRule { CvePattern = "CVE-2024-1234", Source = "debian", Precedence = 5, IsActive = true }, new PrecedenceExceptionRule { CvePattern = "CVE-2024-5678", Source = "debian", Precedence = 5, IsActive = false }, new PrecedenceExceptionRule { CvePattern = "CVE-2024-9999", Source = "debian", Precedence = 5, IsActive = true } ] }; var activeRules = config.GetActiveRules().ToList(); Assert.Equal(2, activeRules.Count); Assert.All(activeRules, r => Assert.True(r.IsActive)); } [Fact] public void FindMatchingRule_ReturnsFirstMatch() { var config = new ExtendedPrecedenceConfig { ExceptionRules = [ new PrecedenceExceptionRule { CvePattern = "CVE-2024-*", Source = "debian", Precedence = 5, IsActive = true }, new PrecedenceExceptionRule { CvePattern = "CVE-2024-1234", Source = "debian", Precedence = 10, IsActive = true } ] }; var rule = config.FindMatchingRule("CVE-2024-1234", "debian"); Assert.NotNull(rule); Assert.Equal(5, rule.Precedence); // First matching rule } [Fact] public void FindMatchingRule_IsCaseInsensitiveForSource() { var config = new ExtendedPrecedenceConfig { ExceptionRules = [ new PrecedenceExceptionRule { CvePattern = "CVE-2024-1234", Source = "debian", Precedence = 5, IsActive = true } ] }; var rule = config.FindMatchingRule("CVE-2024-1234", "DEBIAN"); Assert.NotNull(rule); } [Fact] public void FindMatchingRule_ReturnsNull_WhenNoMatch() { var config = new ExtendedPrecedenceConfig { ExceptionRules = [ new PrecedenceExceptionRule { CvePattern = "CVE-2024-1234", Source = "redhat", Precedence = 5, IsActive = true } ] }; var rule = config.FindMatchingRule("CVE-2024-1234", "debian"); Assert.Null(rule); } }