135 lines
4.1 KiB
C#
135 lines
4.1 KiB
C#
/**
|
|
* Graph Validation & NFC Tests
|
|
* Sprint: SPRINT_9100_0003_0002 (Graph Validation & NFC Normalization)
|
|
* Tasks: VALID-9100-021 through VALID-9100-028
|
|
*/
|
|
|
|
using Xunit;
|
|
|
|
namespace StellaOps.Resolver.Tests;
|
|
|
|
public class GraphValidationTests
|
|
{
|
|
[Fact]
|
|
public void NfcNormalization_ProducesConsistentNodeIds()
|
|
{
|
|
// VALID-9100-021: NFC normalization produces consistent NodeIds
|
|
// Using different Unicode representations of the same character
|
|
// é can be represented as:
|
|
// - U+00E9 (precomposed: LATIN SMALL LETTER E WITH ACUTE)
|
|
// - U+0065 U+0301 (decomposed: e + COMBINING ACUTE ACCENT)
|
|
var precomposed = "caf\u00E9"; // café with precomposed é
|
|
var decomposed = "cafe\u0301"; // café with decomposed é
|
|
|
|
var nodeId1 = NodeId.From("package", precomposed);
|
|
var nodeId2 = NodeId.From("package", decomposed);
|
|
|
|
// After NFC normalization, both should produce the same NodeId
|
|
Assert.Equal(nodeId1, nodeId2);
|
|
}
|
|
|
|
[Fact]
|
|
public void EdgeReferencingNonExistentNode_Detected()
|
|
{
|
|
// VALID-9100-022
|
|
var node1 = Node.Create("package", "a");
|
|
var nonExistentNodeId = NodeId.From("package", "nonexistent");
|
|
|
|
var edge = Edge.Create(node1.Id, "depends_on", nonExistentNodeId);
|
|
|
|
var graph = EvidenceGraph.Create(new[] { node1 }, new[] { edge });
|
|
|
|
var detector = new DefaultImplicitDataDetector();
|
|
var violations = detector.Detect(graph);
|
|
|
|
Assert.Contains(violations, v => v.ViolationType == "DanglingEdgeDestination");
|
|
}
|
|
|
|
[Fact]
|
|
public void DuplicateNodeIds_Detected()
|
|
{
|
|
// VALID-9100-023
|
|
var node1 = Node.Create("package", "a");
|
|
var node2 = new Node(node1.Id, "package", "a-duplicate"); // Same ID, different key
|
|
|
|
var graph = new EvidenceGraph
|
|
{
|
|
Nodes = [node1, node2],
|
|
Edges = []
|
|
};
|
|
|
|
var detector = new DefaultImplicitDataDetector();
|
|
var violations = detector.Detect(graph);
|
|
|
|
Assert.Contains(violations, v => v.ViolationType == "DuplicateNodeId");
|
|
}
|
|
|
|
[Fact]
|
|
public void DuplicateEdgeIds_Detected()
|
|
{
|
|
// VALID-9100-024
|
|
var node1 = Node.Create("package", "a");
|
|
var node2 = Node.Create("package", "b");
|
|
|
|
var edge1 = Edge.Create(node1.Id, "depends_on", node2.Id);
|
|
var edge2 = Edge.Create(node1.Id, "depends_on", node2.Id); // Same EdgeId
|
|
|
|
var graph = new EvidenceGraph
|
|
{
|
|
Nodes = [node1, node2],
|
|
Edges = [edge1, edge2]
|
|
};
|
|
|
|
var detector = new DefaultImplicitDataDetector();
|
|
var violations = detector.Detect(graph);
|
|
|
|
Assert.Contains(violations, v => v.ViolationType == "DuplicateEdgeId");
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidGraph_PassesAllChecks()
|
|
{
|
|
// VALID-9100-027
|
|
var node1 = Node.Create("package", "a");
|
|
var node2 = Node.Create("package", "b");
|
|
var node3 = Node.Create("package", "c");
|
|
|
|
var edge1 = Edge.Create(node1.Id, "depends_on", node2.Id);
|
|
var edge2 = Edge.Create(node2.Id, "depends_on", node3.Id);
|
|
|
|
var graph = EvidenceGraph.Create(new[] { node1, node2, node3 }, new[] { edge1, edge2 });
|
|
|
|
var validator = new DefaultGraphValidator();
|
|
var result = validator.Validate(graph);
|
|
|
|
Assert.True(result.IsValid);
|
|
Assert.Empty(result.Errors);
|
|
}
|
|
|
|
[Fact]
|
|
public void NfcNormalization_IsIdempotent()
|
|
{
|
|
// VALID-9100-028: Property test - NFC is idempotent
|
|
var normalizer = NfcStringNormalizer.Instance;
|
|
var input = "café";
|
|
|
|
var normalized1 = normalizer.Normalize(input);
|
|
var normalized2 = normalizer.Normalize(normalized1);
|
|
var normalized3 = normalizer.Normalize(normalized2);
|
|
|
|
Assert.Equal(normalized1, normalized2);
|
|
Assert.Equal(normalized2, normalized3);
|
|
}
|
|
|
|
[Fact]
|
|
public void EmptyGraph_IsValid()
|
|
{
|
|
var graph = EvidenceGraph.Empty;
|
|
|
|
var validator = new DefaultGraphValidator();
|
|
var result = validator.Validate(graph);
|
|
|
|
Assert.True(result.IsValid);
|
|
}
|
|
}
|