Refactor code structure and optimize performance across multiple modules

This commit is contained in:
StellaOps Bot
2025-12-26 20:03:22 +02:00
parent c786faae84
commit b4fc66feb6
3353 changed files with 88254 additions and 1590657 deletions

View File

@@ -4,13 +4,15 @@ using StellaOps.Concelier.Merge.Identity;
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class AdvisoryIdentityResolverTests
{
private readonly AdvisoryIdentityResolver _resolver = new();
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Resolve_GroupsBySharedCveAlias()
{
var nvd = CreateAdvisory("CVE-2025-1234", aliases: new[] { "CVE-2025-1234" }, source: "nvd");
@@ -24,7 +26,8 @@ public sealed class AdvisoryIdentityResolverTests
Assert.True(cluster.Aliases.Any(alias => alias.Value == "CVE-2025-1234"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Resolve_PrefersPsirtAliasWhenNoCve()
{
var vendor = CreateAdvisory("VMSA-2025-0001", aliases: new[] { "VMSA-2025-0001" }, source: "vmware");
@@ -38,7 +41,8 @@ public sealed class AdvisoryIdentityResolverTests
Assert.True(cluster.Aliases.Any(alias => alias.Value == "VMSA-2025-0001"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Resolve_FallsBackToGhsaWhenOnlyGhsaPresent()
{
var ghsa = CreateAdvisory("GHSA-aaaa-bbbb-cccc", aliases: new[] { "GHSA-aaaa-bbbb-cccc" }, source: "ghsa");
@@ -52,7 +56,8 @@ public sealed class AdvisoryIdentityResolverTests
Assert.True(cluster.Aliases.Any(alias => alias.Value == "GHSA-aaaa-bbbb-cccc"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Resolve_GroupsByKeyWhenNoAliases()
{
var first = CreateAdvisory("custom-1", aliases: Array.Empty<string>(), source: "source-a");

View File

@@ -15,11 +15,13 @@ using StellaOps.Concelier.Storage.Aliases;
using StellaOps.Concelier.Storage.MergeEvents;
using StellaOps.Provenance;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class AdvisoryMergeServiceTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task MergeAsync_AppliesCanonicalRulesAndPersistsDecisions()
{
var aliasStore = new FakeAliasStore();
@@ -167,7 +169,8 @@ public sealed class AdvisoryMergeServiceTests
provenance: new[] { provenance });
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task MergeAsync_PersistsConflictSummariesWithHashes()
{
var aliasStore = new FakeAliasStore();

View File

@@ -9,11 +9,14 @@ using StellaOps.Concelier.Merge.Services;
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class AdvisoryPrecedenceMergerTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_PrefersVendorPrecedenceOverNvd()
{
var timeProvider = new FakeTimeProvider(new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero));
@@ -59,7 +62,8 @@ public sealed class AdvisoryPrecedenceMergerTests
Assert.Contains(severityConflict.Tags, tag => string.Equals(tag.Key, "type", StringComparison.Ordinal) && string.Equals(tag.Value?.ToString(), "severity", StringComparison.OrdinalIgnoreCase));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_KevOnlyTogglesExploitKnown()
{
var timeProvider = new FakeTimeProvider(new DateTimeOffset(2025, 2, 1, 0, 0, 0, TimeSpan.Zero));
@@ -128,7 +132,8 @@ public sealed class AdvisoryPrecedenceMergerTests
Assert.Contains(merged.Provenance, provenance => provenance.Source == "merge");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_UnionsCreditsFromSources()
{
var timeProvider = new FakeTimeProvider();
@@ -231,7 +236,8 @@ public sealed class AdvisoryPrecedenceMergerTests
Assert.Contains(merged.Credits, credit => credit.Provenance.Source == "osv");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_AcscActsAsEnrichmentSource()
{
var timeProvider = new FakeTimeProvider(new DateTimeOffset(2025, 10, 12, 0, 0, 0, TimeSpan.Zero));
@@ -333,7 +339,8 @@ public sealed class AdvisoryPrecedenceMergerTests
Assert.Contains(merged.Provenance, provenance => provenance.Source == "merge" && (provenance.Value?.Contains("acsc", StringComparison.OrdinalIgnoreCase) ?? false));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_RecordsNormalizedRuleMetrics()
{
var now = new DateTimeOffset(2025, 3, 1, 0, 0, 0, TimeSpan.Zero);
@@ -474,7 +481,8 @@ public sealed class AdvisoryPrecedenceMergerTests
Assert.Contains(missingMeasurement.Tags, tag => string.Equals(tag.Key, "package_type", StringComparison.Ordinal) && string.Equals(tag.Value?.ToString(), "semver", StringComparison.Ordinal));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_RespectsConfiguredPrecedenceOverrides()
{
var timeProvider = new FakeTimeProvider(new DateTimeOffset(2025, 3, 1, 0, 0, 0, TimeSpan.Zero));

View File

@@ -4,11 +4,13 @@ using StellaOps.Concelier.Merge.Services;
using StellaOps.Concelier.Models;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class AffectedPackagePrecedenceResolverTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_PrefersRedHatOverNvdForSameCpe()
{
var redHat = new AffectedPackage(
@@ -64,7 +66,8 @@ public sealed class AffectedPackagePrecedenceResolverTests
Assert.Equal(1, rangeOverride.SuppressedRangeCount);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Merge_KeepsNvdWhenNoHigherPrecedence()
{
var nvd = new AffectedPackage(

View File

@@ -6,11 +6,13 @@ using StellaOps.Concelier.Merge.Services;
using StellaOps.Concelier.Storage.Aliases;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class AliasGraphResolverTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_ReturnsCollisions_WhenAliasesOverlap()
{
var aliasStore = new AliasStore();
@@ -39,7 +41,8 @@ public sealed class AliasGraphResolverTests
Assert.Contains("ADV-2", collision.AdvisoryKeys);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task BuildComponentAsync_TracesConnectedAdvisories()
{
var aliasStore = new AliasStore();
@@ -73,7 +76,8 @@ public sealed class AliasGraphResolverTests
Assert.Contains(component.AliasMap["ADV-B"], record => record.Scheme == "OSV" && record.Value == "OSV-2025-1");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task BuildComponentAsync_LinksOsvAndGhsaAliases()
{
var aliasStore = new AliasStore();

View File

@@ -4,18 +4,21 @@ using StellaOps.Concelier.Normalization.Distro;
using StellaOps.VersionComparison;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class ApkVersionComparerTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComparatorType_Returns_Apk()
{
Assert.Equal(ComparatorType.Apk, ApkVersionComparer.Instance.ComparatorType);
}
public static TheoryData<string, string, int, string> ComparisonCases => BuildComparisonCases();
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[MemberData(nameof(ComparisonCases))]
public void Compare_ApkVersions_ReturnsExpectedOrder(string left, string right, int expected, string note)
{
@@ -23,7 +26,8 @@ public sealed class ApkVersionComparerTests
Assert.True(expected == actual, $"[{note}] '{left}' vs '{right}': expected {expected}, got {actual}");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_ParsesApkVersionComponents()
{
var result = ApkVersionComparer.Instance.Compare(
@@ -84,7 +88,8 @@ public sealed class ApkVersionComparerTests
#region CompareWithProof Tests (SPRINT_4000_0002_0001)
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_BothNull_ReturnsEqual()
{
var result = ApkVersionComparer.Instance.CompareWithProof(null, null);
@@ -94,7 +99,8 @@ public sealed class ApkVersionComparerTests
Assert.Contains("null", result.ProofLines[0].ToLower());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_LeftNull_ReturnsLess()
{
var result = ApkVersionComparer.Instance.CompareWithProof(null, "1.0-r0");
@@ -103,7 +109,8 @@ public sealed class ApkVersionComparerTests
Assert.Contains("null", result.ProofLines[0].ToLower());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_RightNull_ReturnsGreater()
{
var result = ApkVersionComparer.Instance.CompareWithProof("1.0-r0", null);
@@ -112,7 +119,8 @@ public sealed class ApkVersionComparerTests
Assert.Contains("null", result.ProofLines[0].ToLower());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_EqualVersions_ReturnsEqualWithProof()
{
var result = ApkVersionComparer.Instance.CompareWithProof("1.2.3-r1", "1.2.3-r1");
@@ -122,7 +130,8 @@ public sealed class ApkVersionComparerTests
Assert.Contains(result.ProofLines, line => line.Contains("equal"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_VersionDifference_ReturnsProofLines()
{
var result = ApkVersionComparer.Instance.CompareWithProof("1.2.3-r0", "1.2.4-r0");
@@ -133,7 +142,8 @@ public sealed class ApkVersionComparerTests
line.Contains("Version") || line.Contains("older") || line.Contains("<"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_PkgRelDifference_ReturnsProofWithPkgRel()
{
var result = ApkVersionComparer.Instance.CompareWithProof("1.2.3-r1", "1.2.3-r2");
@@ -142,7 +152,8 @@ public sealed class ApkVersionComparerTests
Assert.Contains(result.ProofLines, line => line.Contains("release") || line.Contains("-r"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_ImplicitVsExplicitPkgRel_ReturnsProofExplaining()
{
var result = ApkVersionComparer.Instance.CompareWithProof("1.2.3", "1.2.3-r0");
@@ -151,7 +162,8 @@ public sealed class ApkVersionComparerTests
Assert.Contains(result.ProofLines, line => line.Contains("implicit") || line.Contains("explicit"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_NewerVersion_ReturnsGreaterThanOrEqual()
{
var result = ApkVersionComparer.Instance.CompareWithProof("1.2.4-r0", "1.2.3-r0");
@@ -160,7 +172,8 @@ public sealed class ApkVersionComparerTests
Assert.True(result.IsGreaterThanOrEqual);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_InvalidVersions_FallsBackToStringComparison()
{
var result = ApkVersionComparer.Instance.CompareWithProof("", "");
@@ -172,7 +185,8 @@ public sealed class ApkVersionComparerTests
line.Contains("equal", StringComparison.OrdinalIgnoreCase));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompareWithProof_ReturnsCorrectComparatorType()
{
var result = ApkVersionComparer.Instance.CompareWithProof("1.0-r0", "1.0-r1");

View File

@@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using StellaOps.Concelier.Merge.Backport;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
/// <summary>
@@ -35,7 +36,8 @@ public sealed class BackportEvidenceResolverTests
#region Tier 1: DistroAdvisory Evidence
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_Tier1DistroAdvisory_ExtractsEvidence()
{
// Arrange
@@ -60,7 +62,8 @@ public sealed class BackportEvidenceResolverTests
evidence.DistroRelease.Should().Contain("debian");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_Tier1LowConfidence_ReturnsNull()
{
// Arrange
@@ -83,7 +86,8 @@ public sealed class BackportEvidenceResolverTests
#region Tier 2: ChangelogMention Evidence
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_Tier2ChangelogMention_ExtractsEvidence()
{
// Arrange
@@ -108,7 +112,8 @@ public sealed class BackportEvidenceResolverTests
evidence.DistroRelease.Should().Contain("redhat");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_Tier2WithUpstreamCommit_ExtractsPatchLineage()
{
// Arrange
@@ -144,7 +149,8 @@ public sealed class BackportEvidenceResolverTests
#region Tier 3: PatchHeader Evidence
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_Tier3PatchHeader_ExtractsEvidence()
{
// Arrange
@@ -168,7 +174,8 @@ public sealed class BackportEvidenceResolverTests
evidence.PatchOrigin.Should().Be(PatchOrigin.Upstream);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_Tier3DistroPatch_DetectsDistroOrigin()
{
// Arrange
@@ -204,7 +211,8 @@ public sealed class BackportEvidenceResolverTests
#region Tier 4: BinaryFingerprint Evidence
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_Tier4BinaryFingerprint_ExtractsEvidence()
{
// Arrange
@@ -230,7 +238,8 @@ public sealed class BackportEvidenceResolverTests
#region Tier Priority
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_MultipleTiers_SelectsHighestTier()
{
// Arrange: BinaryFingerprint (Tier 4) should be selected as highest
@@ -256,7 +265,8 @@ public sealed class BackportEvidenceResolverTests
evidence!.Tier.Should().Be(BackportEvidenceTier.BinaryFingerprint);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_PatchHeaderVsChangelog_PrefersPatchHeader()
{
// Arrange: PatchHeader (Tier 3) > ChangelogMention (Tier 2)
@@ -286,7 +296,8 @@ public sealed class BackportEvidenceResolverTests
#region Distro Release Extraction
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("pkg:deb/debian/curl@7.64.0-4+deb11u1", "debian:bullseye")]
[InlineData("pkg:deb/debian/openssl@3.0.11-1~deb12u2", "debian:bookworm")]
[InlineData("pkg:rpm/redhat/nginx@1.20.1-14.el9", "redhat:9")]
@@ -314,7 +325,8 @@ public sealed class BackportEvidenceResolverTests
#region Batch Resolution
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveBatchAsync_ResolvesMultiplePackages()
{
// Arrange
@@ -350,7 +362,8 @@ public sealed class BackportEvidenceResolverTests
#region Edge Cases
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_NullProof_ReturnsNull()
{
// Arrange
@@ -365,7 +378,8 @@ public sealed class BackportEvidenceResolverTests
evidence.Should().BeNull();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_VeryLowConfidence_ReturnsNull()
{
// Arrange
@@ -383,7 +397,8 @@ public sealed class BackportEvidenceResolverTests
evidence.Should().BeNull();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task HasEvidenceAsync_ReturnsTrueWhenEvidenceExists()
{
// Arrange
@@ -401,7 +416,8 @@ public sealed class BackportEvidenceResolverTests
hasEvidence.Should().BeTrue();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task HasEvidenceAsync_ReturnsFalseWhenNoEvidence()
{
// Arrange
@@ -416,7 +432,8 @@ public sealed class BackportEvidenceResolverTests
hasEvidence.Should().BeFalse();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_ThrowsOnNullCveId()
{
// Act & Assert
@@ -424,7 +441,8 @@ public sealed class BackportEvidenceResolverTests
() => _resolver.ResolveAsync(null!, "pkg:deb/debian/test@1.0"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ResolveAsync_ThrowsOnNullPurl()
{
// Act & Assert

View File

@@ -15,6 +15,7 @@ using StellaOps.Concelier.Models;
using StellaOps.Concelier.Storage.MergeEvents;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
/// <summary>
@@ -65,7 +66,8 @@ public sealed class BackportProvenanceE2ETests
#region E2E: Debian Backport Advisory Flow
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task E2E_IngestDebianAdvisoryWithBackport_CreatesProvenanceScope()
{
// Arrange: Simulate Debian security advisory for CVE-2024-1234
@@ -129,7 +131,8 @@ public sealed class BackportProvenanceE2ETests
capturedScope.PatchId.Should().Be(patchCommit);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task E2E_IngestRhelAdvisoryWithBackport_CreatesProvenanceScopeWithDistroOrigin()
{
// Arrange: Simulate RHEL security advisory with distro-specific patch
@@ -186,7 +189,8 @@ public sealed class BackportProvenanceE2ETests
#region E2E: Multiple Distro Backports for Same CVE
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task E2E_SameCveMultipleDistros_CreatesSeparateProvenanceScopes()
{
// Arrange: Same CVE with Debian and Ubuntu backports
@@ -239,7 +243,8 @@ public sealed class BackportProvenanceE2ETests
#region E2E: Merge Event with Backport Evidence
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task E2E_MergeWithBackportEvidence_RecordsInAuditLog()
{
// Arrange
@@ -296,7 +301,8 @@ public sealed class BackportProvenanceE2ETests
#region E2E: Evidence Tier Upgrade
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task E2E_EvidenceUpgrade_UpdatesProvenanceScope()
{
// Arrange: Start with low-tier evidence, then upgrade
@@ -355,7 +361,8 @@ public sealed class BackportProvenanceE2ETests
#region E2E: Provenance Retrieval
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task E2E_RetrieveProvenanceForCanonical_ReturnsAllDistroScopes()
{
// Arrange

View File

@@ -2,6 +2,7 @@ using System.Linq;
using StellaOps.Concelier.Merge.Services;
using StellaOps.Concelier.Models;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class CanonicalHashCalculatorTests
@@ -41,7 +42,8 @@ public sealed class CanonicalHashCalculatorTests
},
provenance: new[] { AdvisoryProvenance.Empty });
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeHash_ReturnsDeterministicValue()
{
var calculator = new CanonicalHashCalculator();
@@ -51,7 +53,8 @@ public sealed class CanonicalHashCalculatorTests
Assert.Equal(first, second);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeHash_IgnoresOrderingDifferences()
{
var calculator = new CanonicalHashCalculator();
@@ -77,7 +80,8 @@ public sealed class CanonicalHashCalculatorTests
Assert.Equal(originalHash, reorderedHash);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeHash_NullReturnsEmpty()
{
var calculator = new CanonicalHashCalculator();

View File

@@ -3,11 +3,13 @@ using StellaOps.Concelier.Merge.Comparers;
using StellaOps.Concelier.Normalization.Distro;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class DebianEvrComparerTests
{
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("1:1.2.3-1", 1, "1.2.3", "1")]
[InlineData("1.2.3-1", 0, "1.2.3", "1")]
[InlineData("2:4.5", 2, "4.5", "")]
@@ -24,7 +26,8 @@ public sealed class DebianEvrComparerTests
Assert.Equal(input, evr.Original);
}
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("")]
[InlineData(":1.0-1")]
[InlineData("1:")]
@@ -36,7 +39,8 @@ public sealed class DebianEvrComparerTests
Assert.Null(evr);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_PrefersHigherEpoch()
{
var lower = "0:2.0-1";
@@ -45,7 +49,8 @@ public sealed class DebianEvrComparerTests
Assert.True(DebianEvrComparer.Instance.Compare(higher, lower) > 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_UsesVersionOrdering()
{
var lower = "0:1.2.3-1";
@@ -54,7 +59,8 @@ public sealed class DebianEvrComparerTests
Assert.True(DebianEvrComparer.Instance.Compare(higher, lower) > 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_TildeRanksEarlier()
{
var prerelease = "0:1.0~beta1-1";
@@ -63,7 +69,8 @@ public sealed class DebianEvrComparerTests
Assert.True(DebianEvrComparer.Instance.Compare(prerelease, stable) < 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_RevisionBreaksTies()
{
var first = "0:1.0-1";
@@ -72,7 +79,8 @@ public sealed class DebianEvrComparerTests
Assert.True(DebianEvrComparer.Instance.Compare(second, first) > 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_FallsBackToOrdinalForInvalid()
{
var left = "not-an-evr";
@@ -86,7 +94,8 @@ public sealed class DebianEvrComparerTests
public static TheoryData<string, string, int, string> ComparisonCases => BuildComparisonCases();
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[MemberData(nameof(ComparisonCases))]
public void Compare_DebianEvr_ReturnsExpectedOrder(string left, string right, int expected, string note)
{

View File

@@ -7,6 +7,7 @@ using System.Text;
using StellaOps.Concelier.Merge.Comparers;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class GoldenVersionComparisonTests
@@ -16,7 +17,8 @@ public sealed class GoldenVersionComparisonTests
PropertyNameCaseInsensitive = true
};
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("rpm_version_comparison.golden.ndjson", "rpm")]
[InlineData("deb_version_comparison.golden.ndjson", "deb")]
[InlineData("apk_version_comparison.golden.ndjson", "apk")]

View File

@@ -4,11 +4,13 @@ using StellaOps.Concelier.Merge.Services;
using StellaOps.Concelier.Models;
using StellaOps.Concelier.Storage.MergeEvents;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class MergeEventWriterTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AppendAsync_WritesRecordWithComputedHashes()
{
var store = new InMemoryMergeEventStore();
@@ -31,7 +33,8 @@ public sealed class MergeEventWriterTests
Assert.Same(store.LastRecord, record);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AppendAsync_NullBeforeUsesEmptyHash()
{
var store = new InMemoryMergeEventStore();

View File

@@ -9,6 +9,7 @@ using FluentAssertions;
using StellaOps.Concelier.Merge.Identity;
using StellaOps.Concelier.Merge.Identity.Normalizers;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
/// <summary>
@@ -26,7 +27,8 @@ public sealed class MergeHashBackportDifferentiationTests
#region Same Patch Lineage = Same Hash
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_SamePatchLineage_ProducesSameHash()
{
// Arrange
@@ -56,7 +58,8 @@ public sealed class MergeHashBackportDifferentiationTests
hash1.Should().Be(hash2, "same patch lineage should produce same hash");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_NoPatchLineage_ProducesSameHash()
{
// Arrange
@@ -90,7 +93,8 @@ public sealed class MergeHashBackportDifferentiationTests
#region Different Patch Lineage = Different Hash
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_DifferentPatchLineage_ProducesDifferentHash()
{
// Arrange - Upstream fix vs distro-specific backport
@@ -121,7 +125,8 @@ public sealed class MergeHashBackportDifferentiationTests
"different patch lineage should produce different hash");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_WithVsWithoutPatchLineage_ProducesDifferentHash()
{
// Arrange
@@ -152,7 +157,8 @@ public sealed class MergeHashBackportDifferentiationTests
"advisory with patch lineage should differ from one without");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_DebianVsRhelBackport_ProducesDifferentHash()
{
// Arrange - Same CVE, different distro backports
@@ -187,7 +193,8 @@ public sealed class MergeHashBackportDifferentiationTests
#region Patch Lineage Normalization
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData(
"abc123def456abc123def456abc123def456abcd",
"ABC123DEF456ABC123DEF456ABC123DEF456ABCD",
@@ -230,7 +237,8 @@ public sealed class MergeHashBackportDifferentiationTests
hash1.Should().Be(hash2, reason);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_AbbreviatedSha_DiffersFromFullSha()
{
// Abbreviated SHA is treated as different from a full different SHA
@@ -265,7 +273,8 @@ public sealed class MergeHashBackportDifferentiationTests
#region Real-World Scenarios
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_GoldenCorpus_DebianBackportVsNvd()
{
// Golden corpus test case: CVE-2024-1234 with Debian backport
@@ -300,7 +309,8 @@ public sealed class MergeHashBackportDifferentiationTests
"NVD and Debian entries should produce different hashes due to package and version differences");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_GoldenCorpus_DistroSpecificFix()
{
// Golden corpus test case: Distro-specific fix different from upstream
@@ -331,7 +341,8 @@ public sealed class MergeHashBackportDifferentiationTests
"distro-specific fix should produce different hash from upstream");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_SameUpstreamBackported_ProducesSameHash()
{
// When two distros backport the SAME upstream patch, they should merge
@@ -367,7 +378,8 @@ public sealed class MergeHashBackportDifferentiationTests
#region Edge Cases
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_EmptyPatchLineage_TreatedAsNull()
{
var emptyLineage = new MergeHashInput
@@ -397,7 +409,8 @@ public sealed class MergeHashBackportDifferentiationTests
"empty and null patch lineage should produce same hash");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_WhitespacePatchLineage_TreatedAsNull()
{
var whitespaceLineage = new MergeHashInput
@@ -427,7 +440,8 @@ public sealed class MergeHashBackportDifferentiationTests
"whitespace-only patch lineage should be treated as null");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeMergeHash_IsDeterministic()
{
// Verify determinism across multiple calls

View File

@@ -7,6 +7,7 @@ using StellaOps.Concelier.Merge.Services;
using StellaOps.Concelier.Models;
using StellaOps.Concelier.Storage.MergeEvents;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class MergePrecedenceIntegrationTests : IAsyncLifetime
@@ -16,7 +17,8 @@ public sealed class MergePrecedenceIntegrationTests : IAsyncLifetime
private AdvisoryPrecedenceMerger? _merger;
private FakeTimeProvider? _timeProvider;
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task MergePipeline_PsirtOverridesNvd_AndKevOnlyTogglesExploitKnown()
{
await EnsureInitializedAsync();

View File

@@ -3,11 +3,13 @@ using StellaOps.Concelier.Merge.Comparers;
using StellaOps.Concelier.Normalization.Distro;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class NevraComparerTests
{
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("kernel-1:4.18.0-348.7.1.el8_5.x86_64", "kernel", 1, "4.18.0", "348.7.1.el8_5", "x86_64")]
[InlineData("bash-5.1.8-2.fc35.x86_64", "bash", 0, "5.1.8", "2.fc35", "x86_64")]
[InlineData("openssl-libs-1:1.1.1k-7.el8", "openssl-libs", 1, "1.1.1k", "7.el8", null)]
@@ -28,7 +30,8 @@ public sealed class NevraComparerTests
Assert.Equal(input, nevra.Original);
}
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("")]
[InlineData("kernel4.18.0-80.el8")]
[InlineData("kernel-4.18.0")]
@@ -40,7 +43,8 @@ public sealed class NevraComparerTests
Assert.Null(nevra);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TryParse_TrimsWhitespace()
{
var success = Nevra.TryParse(" kernel-0:4.18.0-80.el8.x86_64 ", out var nevra);
@@ -51,7 +55,8 @@ public sealed class NevraComparerTests
Assert.Equal("4.18.0", nevra.Version);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_PrefersHigherEpoch()
{
var older = "kernel-0:4.18.0-348.7.1.el8_5.x86_64";
@@ -61,7 +66,8 @@ public sealed class NevraComparerTests
Assert.True(NevraComparer.Instance.Compare(older, newer) < 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_UsesRpmVersionOrdering()
{
var lower = "kernel-0:4.18.0-80.el8.x86_64";
@@ -70,7 +76,8 @@ public sealed class NevraComparerTests
Assert.True(NevraComparer.Instance.Compare(higher, lower) > 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_UsesReleaseOrdering()
{
var el8 = "bash-0:5.1.0-1.el8.x86_64";
@@ -79,7 +86,8 @@ public sealed class NevraComparerTests
Assert.True(NevraComparer.Instance.Compare(el9, el8) > 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_TildeRanksEarlier()
{
var prerelease = "bash-0:5.1.0~beta-1.fc34.x86_64";
@@ -88,7 +96,8 @@ public sealed class NevraComparerTests
Assert.True(NevraComparer.Instance.Compare(prerelease, stable) < 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_ConsidersArchitecture()
{
var noarch = "pkg-0:1.0-1.noarch";
@@ -97,7 +106,8 @@ public sealed class NevraComparerTests
Assert.True(NevraComparer.Instance.Compare(noarch, arch) < 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_FallsBackToOrdinalForInvalid()
{
var left = "not-a-nevra";
@@ -110,7 +120,8 @@ public sealed class NevraComparerTests
public static TheoryData<string, string, int, string> ComparisonCases => BuildComparisonCases();
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[MemberData(nameof(ComparisonCases))]
public void Compare_NevraVersions_ReturnsExpectedOrder(string left, string right, int expected, string note)
{

View File

@@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using StellaOps.Concelier.Merge.Backport;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
/// <summary>
@@ -34,7 +35,8 @@ public sealed class ProvenanceScopeLifecycleTests
#region CreateOrUpdateAsync Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CreateOrUpdateAsync_NewScope_CreatesProvenanceScope()
{
// Arrange
@@ -74,7 +76,8 @@ public sealed class ProvenanceScopeLifecycleTests
Times.Once);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CreateOrUpdateAsync_ExistingScope_UpdatesProvenanceScope()
{
// Arrange
@@ -117,7 +120,8 @@ public sealed class ProvenanceScopeLifecycleTests
result.ProvenanceScopeId.Should().Be(existingScopeId);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CreateOrUpdateAsync_WithEvidenceResolver_ResolvesEvidence()
{
// Arrange
@@ -171,7 +175,8 @@ public sealed class ProvenanceScopeLifecycleTests
Times.Once);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CreateOrUpdateAsync_NonDistroSource_StillCreatesScope()
{
// Arrange
@@ -204,7 +209,8 @@ public sealed class ProvenanceScopeLifecycleTests
#region UpdateFromEvidenceAsync Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task UpdateFromEvidenceAsync_NewEvidence_CreatesScope()
{
// Arrange
@@ -246,7 +252,8 @@ public sealed class ProvenanceScopeLifecycleTests
Times.Once);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task UpdateFromEvidenceAsync_BetterEvidence_UpdatesScope()
{
// Arrange
@@ -300,7 +307,8 @@ public sealed class ProvenanceScopeLifecycleTests
Times.Once);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task UpdateFromEvidenceAsync_LowerConfidenceEvidence_SkipsUpdate()
{
// Arrange
@@ -352,7 +360,8 @@ public sealed class ProvenanceScopeLifecycleTests
#region LinkEvidenceRefAsync Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task LinkEvidenceRefAsync_LinksEvidenceToScope()
{
// Arrange
@@ -374,7 +383,8 @@ public sealed class ProvenanceScopeLifecycleTests
#region GetByCanonicalIdAsync Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GetByCanonicalIdAsync_ReturnsAllScopes()
{
// Arrange
@@ -418,7 +428,8 @@ public sealed class ProvenanceScopeLifecycleTests
#region DeleteByCanonicalIdAsync Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task DeleteByCanonicalIdAsync_DeletesAllScopes()
{
// Arrange
@@ -439,7 +450,8 @@ public sealed class ProvenanceScopeLifecycleTests
#region Distro Release Extraction Tests
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("pkg:deb/debian/curl@7.64.0-4+deb11u1", "debian", "debian:bullseye")]
[InlineData("pkg:deb/debian/openssl@3.0.11-1~deb12u2", "debian", "debian:bookworm")]
[InlineData("pkg:rpm/redhat/nginx@1.20.1-14.el9", "redhat", "redhat:9")]

View File

@@ -2,11 +2,13 @@ using FluentAssertions;
using StellaOps.Concelier.Merge.Comparers;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Concelier.Merge.Tests;
public sealed class SemanticVersionRangeResolverTests
{
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("1.2.3", true)]
[InlineData("1.2.3-beta.1", true)]
[InlineData("invalid", false)]
@@ -19,14 +21,16 @@ public sealed class SemanticVersionRangeResolverTests
Assert.Equal(expected, version is not null);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_ParsesSemanticVersions()
{
Assert.True(SemanticVersionRangeResolver.Compare("1.2.3", "1.2.2") > 0);
Assert.True(SemanticVersionRangeResolver.Compare("1.2.3-beta", "1.2.3") < 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Compare_UsesOrdinalFallbackForInvalid()
{
var left = "zzz";
@@ -37,7 +41,8 @@ public sealed class SemanticVersionRangeResolverTests
Assert.Equal(expected, actual);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResolveWindows_WithFixedVersion_ComputesExclusiveUpper()
{
var (introduced, exclusive, inclusive) = SemanticVersionRangeResolver.ResolveWindows("1.0.0", "1.2.0", null);
@@ -47,7 +52,8 @@ public sealed class SemanticVersionRangeResolverTests
Assert.Null(inclusive);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResolveWindows_WithLastAffectedOnly_ComputesInclusiveAndExclusive()
{
var (introduced, exclusive, inclusive) = SemanticVersionRangeResolver.ResolveWindows("1.0.0", null, "1.1.5");
@@ -57,7 +63,8 @@ public sealed class SemanticVersionRangeResolverTests
Assert.Equal(SemanticVersionRangeResolver.Parse("1.1.5"), inclusive);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ResolveWindows_WithNeither_ReturnsNullBounds()
{
var (introduced, exclusive, inclusive) = SemanticVersionRangeResolver.ResolveWindows(null, null, null);

View File

@@ -12,6 +12,7 @@
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.Canonical.Json/StellaOps.Canonical.Json.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />