Files
git.stella-ops.org/src/BinaryIndex/__Tests/StellaOps.BinaryIndex.Diff.Tests/Unit/DiffEvidenceTests.cs
master 7f7eb8b228 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>
2026-01-11 10:09:07 +02:00

276 lines
8.2 KiB
C#

// 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 DiffEvidenceTests
{
[Fact]
public void FunctionRemoved_CreatesCorrectEvidence()
{
// Act
var evidence = DiffEvidence.FunctionRemoved("vuln_func");
// Assert
evidence.Type.Should().Be(DiffEvidenceType.FunctionRemoved);
evidence.FunctionName.Should().Be("vuln_func");
evidence.Weight.Should().Be(0.9m);
evidence.Description.Should().Contain("vuln_func");
}
[Fact]
public void FunctionRenamed_CreatesCorrectEvidence()
{
// Act
var evidence = DiffEvidence.FunctionRenamed("old_name", "new_name", 0.85m);
// Assert
evidence.Type.Should().Be(DiffEvidenceType.FunctionRenamed);
evidence.FunctionName.Should().Be("old_name");
evidence.Data["OldName"].Should().Be("old_name");
evidence.Data["NewName"].Should().Be("new_name");
evidence.Data["Similarity"].Should().Be("0.850");
}
[Fact]
public void CfgStructureChanged_CreatesCorrectEvidence()
{
// Act
var evidence = DiffEvidence.CfgStructureChanged("func", "hash1", "hash2");
// Assert
evidence.Type.Should().Be(DiffEvidenceType.CfgStructureChanged);
evidence.FunctionName.Should().Be("func");
evidence.Data["PreHash"].Should().Be("hash1");
evidence.Data["PostHash"].Should().Be("hash2");
evidence.Weight.Should().Be(0.5m);
}
[Fact]
public void VulnerableEdgeRemoved_CreatesCorrectEvidence()
{
// Arrange
var edges = ImmutableArray.Create("bb0->bb1", "bb1->bb2");
// Act
var evidence = DiffEvidence.VulnerableEdgeRemoved("func", edges);
// Assert
evidence.Type.Should().Be(DiffEvidenceType.VulnerableEdgeRemoved);
evidence.Weight.Should().Be(1.0m);
evidence.Data["EdgeCount"].Should().Be("2");
evidence.Data["EdgesRemoved"].Should().Contain("bb0->bb1");
}
[Fact]
public void SinkMadeUnreachable_CreatesCorrectEvidence()
{
// Arrange
var sinks = ImmutableArray.Create("memcpy", "strcpy");
// Act
var evidence = DiffEvidence.SinkMadeUnreachable("func", sinks);
// Assert
evidence.Type.Should().Be(DiffEvidenceType.SinkMadeUnreachable);
evidence.Weight.Should().Be(0.95m);
evidence.Data["SinkCount"].Should().Be("2");
}
[Fact]
public void TaintGateAdded_CreatesCorrectEvidence()
{
// Act
var evidence = DiffEvidence.TaintGateAdded("func", "BoundCheck", "len < bufsize");
// Assert
evidence.Type.Should().Be(DiffEvidenceType.TaintGateAdded);
evidence.Data["GateType"].Should().Be("BoundCheck");
evidence.Data["Condition"].Should().Be("len < bufsize");
evidence.Weight.Should().Be(0.85m);
}
[Fact]
public void SemanticDivergence_CreatesCorrectEvidence()
{
// Act
var evidence = DiffEvidence.SemanticDivergence("func", 0.45m);
// Assert
evidence.Type.Should().Be(DiffEvidenceType.SemanticDivergence);
evidence.Data["Similarity"].Should().Be("0.450");
evidence.Weight.Should().Be(0.6m);
}
[Fact]
public void IdenticalBinaries_CreatesCorrectEvidence()
{
// Act
var evidence = DiffEvidence.IdenticalBinaries("sha256:abc123");
// Assert
evidence.Type.Should().Be(DiffEvidenceType.IdenticalBinaries);
evidence.Data["Digest"].Should().Be("sha256:abc123");
evidence.Weight.Should().Be(1.0m);
}
[Theory]
[InlineData(DiffEvidenceType.FunctionRemoved)]
[InlineData(DiffEvidenceType.FunctionRenamed)]
[InlineData(DiffEvidenceType.CfgStructureChanged)]
[InlineData(DiffEvidenceType.VulnerableEdgeRemoved)]
[InlineData(DiffEvidenceType.VulnerableBlockModified)]
[InlineData(DiffEvidenceType.SinkMadeUnreachable)]
[InlineData(DiffEvidenceType.TaintGateAdded)]
[InlineData(DiffEvidenceType.ConstantChanged)]
[InlineData(DiffEvidenceType.SemanticDivergence)]
[InlineData(DiffEvidenceType.IdenticalBinaries)]
public void DiffEvidenceType_AllValuesAreDefined(DiffEvidenceType type)
{
// Assert
Enum.IsDefined(type).Should().BeTrue();
}
}
[Trait("Category", "Unit")]
public sealed class DiffOptionsTests
{
[Fact]
public void Default_HasSensibleDefaults()
{
// Act
var options = DiffOptions.Default;
// Assert
options.IncludeSemanticAnalysis.Should().BeFalse();
options.IncludeReachabilityAnalysis.Should().BeTrue();
options.SemanticThreshold.Should().Be(0.85m);
options.FixedConfidenceThreshold.Should().Be(0.80m);
options.DetectRenames.Should().BeTrue();
options.FunctionTimeout.Should().Be(TimeSpan.FromSeconds(30));
options.TotalTimeout.Should().Be(TimeSpan.FromMinutes(10));
}
[Fact]
public void DiffOptions_CanBeCustomized()
{
// Act
var options = new DiffOptions
{
IncludeSemanticAnalysis = true,
SemanticThreshold = 0.95m,
DetectRenames = false
};
// Assert
options.IncludeSemanticAnalysis.Should().BeTrue();
options.SemanticThreshold.Should().Be(0.95m);
options.DetectRenames.Should().BeFalse();
}
}
[Trait("Category", "Unit")]
public sealed class DiffMetadataTests
{
[Fact]
public void CurrentEngineVersion_IsSet()
{
// Assert
DiffMetadata.CurrentEngineVersion.Should().NotBeNullOrEmpty();
DiffMetadata.CurrentEngineVersion.Should().Be("1.0.0");
}
[Fact]
public void DiffMetadata_StoresAllProperties()
{
// Arrange
var comparedAt = DateTimeOffset.UtcNow;
var duration = TimeSpan.FromSeconds(5);
var options = DiffOptions.Default;
// Act
var metadata = new DiffMetadata
{
ComparedAt = comparedAt,
EngineVersion = DiffMetadata.CurrentEngineVersion,
Duration = duration,
Options = options
};
// Assert
metadata.ComparedAt.Should().Be(comparedAt);
metadata.EngineVersion.Should().Be("1.0.0");
metadata.Duration.Should().Be(duration);
metadata.Options.Should().Be(options);
}
}
[Trait("Category", "Unit")]
public sealed class SingleBinaryCheckResultTests
{
[Fact]
public void NotVulnerable_CreatesCorrectResult()
{
// Arrange
var binaryDigest = "sha256:abc123";
var goldenSetId = "CVE-2024-1234";
var checkedAt = DateTimeOffset.UtcNow;
var duration = TimeSpan.FromMilliseconds(50);
// Act
var result = SingleBinaryCheckResult.NotVulnerable(
binaryDigest, goldenSetId, checkedAt, duration);
// Assert
result.IsVulnerable.Should().BeFalse();
result.Confidence.Should().Be(0.9m);
result.BinaryDigest.Should().Be(binaryDigest);
result.GoldenSetId.Should().Be(goldenSetId);
result.FunctionResults.Should().BeEmpty();
}
}
[Trait("Category", "Unit")]
public sealed class FunctionRenameTests
{
[Fact]
public void FunctionRename_StoresAllProperties()
{
// Act
var rename = new FunctionRename
{
OriginalName = "old_func",
NewName = "new_func",
Confidence = 0.92m,
Similarity = 0.92m
};
// Assert
rename.OriginalName.Should().Be("old_func");
rename.NewName.Should().Be("new_func");
rename.Confidence.Should().Be(0.92m);
rename.Similarity.Should().Be(0.92m);
}
}
[Trait("Category", "Unit")]
public sealed class RenameDetectionOptionsTests
{
[Fact]
public void Default_HasSensibleDefaults()
{
// Act
var options = RenameDetectionOptions.Default;
// Assert
options.MinSimilarity.Should().Be(0.7m);
options.UseCfgHash.Should().BeTrue();
options.UseBlockHashes.Should().BeTrue();
options.UseStringRefs.Should().BeTrue();
}
}