save dev progress
This commit is contained in:
@@ -0,0 +1,450 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// 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<ConfigurableSourcePrecedenceLattice> _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<ArgumentException>(() => 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user