Complete batch 012 (golden set diff) and 013 (advisory chat), fix build errors
Sprints completed: - SPRINT_20260110_012_* (golden set diff layer - 10 sprints) - SPRINT_20260110_013_* (advisory chat - 4 sprints) Build fixes applied: - Fix namespace conflicts with Microsoft.Extensions.Options.Options.Create - Fix VexDecisionReachabilityIntegrationTests API drift (major rewrite) - Fix VexSchemaValidationTests FluentAssertions method name - Fix FixChainGateIntegrationTests ambiguous type references - Fix AdvisoryAI test files required properties and namespace aliases - Add stub types for CveMappingController (ICveSymbolMappingService) - Fix VerdictBuilderService static context issue Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
// Licensed under AGPL-3.0-or-later. Copyright (C) 2026 StellaOps Contributors.
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.BinaryIndex.Diff.Tests.Unit;
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class PatchDiffModelTests
|
||||
{
|
||||
[Fact]
|
||||
public void PatchDiffResult_NoPatchDetected_CreatesCorrectResult()
|
||||
{
|
||||
// Arrange
|
||||
var goldenSetId = "CVE-2024-1234";
|
||||
var goldenSetDigest = "sha256:abcd1234";
|
||||
var binaryDigest = "sha256:same1234";
|
||||
var comparedAt = DateTimeOffset.UtcNow;
|
||||
var duration = TimeSpan.FromMilliseconds(100);
|
||||
var options = DiffOptions.Default;
|
||||
|
||||
// Act
|
||||
var result = PatchDiffResult.NoPatchDetected(
|
||||
goldenSetId, goldenSetDigest, binaryDigest,
|
||||
comparedAt, duration, options);
|
||||
|
||||
// Assert
|
||||
result.GoldenSetId.Should().Be(goldenSetId);
|
||||
result.Verdict.Should().Be(PatchVerdict.NoPatchDetected);
|
||||
result.Confidence.Should().Be(1.0m);
|
||||
result.PreBinaryDigest.Should().Be(binaryDigest);
|
||||
result.PostBinaryDigest.Should().Be(binaryDigest);
|
||||
result.Evidence.Should().HaveCount(1);
|
||||
result.Evidence[0].Type.Should().Be(DiffEvidenceType.IdenticalBinaries);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(PatchVerdict.Fixed)]
|
||||
[InlineData(PatchVerdict.PartialFix)]
|
||||
[InlineData(PatchVerdict.StillVulnerable)]
|
||||
[InlineData(PatchVerdict.Inconclusive)]
|
||||
[InlineData(PatchVerdict.NoPatchDetected)]
|
||||
public void PatchVerdict_AllValuesAreDefined(PatchVerdict verdict)
|
||||
{
|
||||
// Assert
|
||||
Enum.IsDefined(verdict).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FunctionDiffResult_FunctionRemoved_CreatesCorrectResult()
|
||||
{
|
||||
// Act
|
||||
var result = FunctionDiffResult.FunctionRemoved("vulnerable_func");
|
||||
|
||||
// Assert
|
||||
result.FunctionName.Should().Be("vulnerable_func");
|
||||
result.PreStatus.Should().Be(FunctionStatus.Present);
|
||||
result.PostStatus.Should().Be(FunctionStatus.Absent);
|
||||
result.Verdict.Should().Be(FunctionPatchVerdict.FunctionRemoved);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FunctionDiffResult_NotFound_CreatesCorrectResult()
|
||||
{
|
||||
// Act
|
||||
var result = FunctionDiffResult.NotFound("missing_func");
|
||||
|
||||
// Assert
|
||||
result.FunctionName.Should().Be("missing_func");
|
||||
result.PreStatus.Should().Be(FunctionStatus.Absent);
|
||||
result.PostStatus.Should().Be(FunctionStatus.Absent);
|
||||
result.Verdict.Should().Be(FunctionPatchVerdict.Inconclusive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CfgDiffResult_StructureChanged_DetectsChange()
|
||||
{
|
||||
// Arrange
|
||||
var diff = new CfgDiffResult
|
||||
{
|
||||
PreCfgHash = "hash1",
|
||||
PostCfgHash = "hash2",
|
||||
PreBlockCount = 5,
|
||||
PostBlockCount = 6,
|
||||
PreEdgeCount = 7,
|
||||
PostEdgeCount = 9
|
||||
};
|
||||
|
||||
// Assert
|
||||
diff.StructureChanged.Should().BeTrue();
|
||||
diff.BlockCountDelta.Should().Be(1);
|
||||
diff.EdgeCountDelta.Should().Be(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CfgDiffResult_NoStructureChange_WhenHashesMatch()
|
||||
{
|
||||
// Arrange
|
||||
var diff = new CfgDiffResult
|
||||
{
|
||||
PreCfgHash = "samehash",
|
||||
PostCfgHash = "samehash",
|
||||
PreBlockCount = 5,
|
||||
PostBlockCount = 5,
|
||||
PreEdgeCount = 7,
|
||||
PostEdgeCount = 7
|
||||
};
|
||||
|
||||
// Assert
|
||||
diff.StructureChanged.Should().BeFalse();
|
||||
diff.BlockCountDelta.Should().Be(0);
|
||||
diff.EdgeCountDelta.Should().Be(0);
|
||||
}
|
||||
}
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class VulnerableEdgeDiffTests
|
||||
{
|
||||
[Fact]
|
||||
public void Compute_AllEdgesRemoved_SetsFlag()
|
||||
{
|
||||
// Arrange
|
||||
var preEdges = ImmutableArray.Create("bb0->bb1", "bb1->bb2");
|
||||
var postEdges = ImmutableArray<string>.Empty;
|
||||
|
||||
// Act
|
||||
var diff = VulnerableEdgeDiff.Compute(preEdges, postEdges);
|
||||
|
||||
// Assert
|
||||
diff.AllVulnerableEdgesRemoved.Should().BeTrue();
|
||||
diff.EdgesRemoved.Should().HaveCount(2);
|
||||
diff.EdgesAdded.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Compute_SomeEdgesRemoved_SetsFlag()
|
||||
{
|
||||
// Arrange
|
||||
var preEdges = ImmutableArray.Create("bb0->bb1", "bb1->bb2");
|
||||
var postEdges = ImmutableArray.Create("bb0->bb1");
|
||||
|
||||
// Act
|
||||
var diff = VulnerableEdgeDiff.Compute(preEdges, postEdges);
|
||||
|
||||
// Assert
|
||||
diff.AllVulnerableEdgesRemoved.Should().BeFalse();
|
||||
diff.SomeVulnerableEdgesRemoved.Should().BeTrue();
|
||||
diff.EdgesRemoved.Should().Contain("bb1->bb2");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Compute_NoChange_NoEdgesRemovedOrAdded()
|
||||
{
|
||||
// Arrange
|
||||
var edges = ImmutableArray.Create("bb0->bb1", "bb1->bb2");
|
||||
|
||||
// Act
|
||||
var diff = VulnerableEdgeDiff.Compute(edges, edges);
|
||||
|
||||
// Assert
|
||||
diff.NoChange.Should().BeTrue();
|
||||
diff.EdgesRemoved.Should().BeEmpty();
|
||||
diff.EdgesAdded.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Compute_EdgesAdded_TracksNewEdges()
|
||||
{
|
||||
// Arrange
|
||||
var preEdges = ImmutableArray.Create("bb0->bb1");
|
||||
var postEdges = ImmutableArray.Create("bb0->bb1", "bb1->bb3");
|
||||
|
||||
// Act
|
||||
var diff = VulnerableEdgeDiff.Compute(preEdges, postEdges);
|
||||
|
||||
// Assert
|
||||
diff.EdgesAdded.Should().Contain("bb1->bb3");
|
||||
diff.AllVulnerableEdgesRemoved.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Empty_ReturnsEmptyDiff()
|
||||
{
|
||||
// Act
|
||||
var empty = VulnerableEdgeDiff.Empty;
|
||||
|
||||
// Assert
|
||||
empty.EdgesInPre.Should().BeEmpty();
|
||||
empty.EdgesInPost.Should().BeEmpty();
|
||||
empty.EdgesRemoved.Should().BeEmpty();
|
||||
empty.EdgesAdded.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class SinkReachabilityDiffTests
|
||||
{
|
||||
[Fact]
|
||||
public void Compute_AllSinksUnreachable_SetsFlag()
|
||||
{
|
||||
// Arrange
|
||||
var preSinks = ImmutableArray.Create("memcpy", "strcpy");
|
||||
var postSinks = ImmutableArray<string>.Empty;
|
||||
|
||||
// Act
|
||||
var diff = SinkReachabilityDiff.Compute(preSinks, postSinks);
|
||||
|
||||
// Assert
|
||||
diff.AllSinksUnreachable.Should().BeTrue();
|
||||
diff.SinksMadeUnreachable.Should().HaveCount(2);
|
||||
diff.SinksStillReachable.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Compute_SomeSinksUnreachable_SetsFlag()
|
||||
{
|
||||
// Arrange
|
||||
var preSinks = ImmutableArray.Create("memcpy", "strcpy");
|
||||
var postSinks = ImmutableArray.Create("memcpy");
|
||||
|
||||
// Act
|
||||
var diff = SinkReachabilityDiff.Compute(preSinks, postSinks);
|
||||
|
||||
// Assert
|
||||
diff.AllSinksUnreachable.Should().BeFalse();
|
||||
diff.SomeSinksUnreachable.Should().BeTrue();
|
||||
diff.SinksMadeUnreachable.Should().Contain("strcpy");
|
||||
diff.SinksStillReachable.Should().Contain("memcpy");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Empty_ReturnsEmptyDiff()
|
||||
{
|
||||
// Act
|
||||
var empty = SinkReachabilityDiff.Empty;
|
||||
|
||||
// Assert
|
||||
empty.SinksReachableInPre.Should().BeEmpty();
|
||||
empty.SinksReachableInPost.Should().BeEmpty();
|
||||
empty.SinksMadeUnreachable.Should().BeEmpty();
|
||||
empty.SinksStillReachable.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user