using System.Text.Json.Nodes; using StellaOps.Findings.Ledger.Domain; using StellaOps.Findings.Ledger.Hashing; using Xunit; namespace StellaOps.Findings.Ledger.Tests; public sealed class ProjectionHashingTests { [Fact] public void ComputeCycleHash_IncludesRiskFields() { var projection = CreateProjection(riskScore: 5.5m, riskSeverity: "high"); var hashWithRisk = ProjectionHashing.ComputeCycleHash(projection); var changedRisk = projection with { RiskScore = 4.0m }; var hashChangedRisk = ProjectionHashing.ComputeCycleHash(changedRisk); Assert.NotEqual(hashWithRisk, hashChangedRisk); } [Fact] public void ComputeCycleHash_ChangesWhenRiskExplanationChanges() { var projection = CreateProjection(riskExplanationId: Guid.NewGuid()); var hashWithExplanation = ProjectionHashing.ComputeCycleHash(projection); var projectionDifferent = projection with { RiskExplanationId = Guid.NewGuid() }; var hashWithDifferentExplanation = ProjectionHashing.ComputeCycleHash(projectionDifferent); Assert.NotEqual(hashWithExplanation, hashWithDifferentExplanation); } private static FindingProjection CreateProjection(decimal? riskScore = null, string? riskSeverity = null, Guid? riskExplanationId = null) { return new FindingProjection( TenantId: "t1", FindingId: "f1", PolicyVersion: "v1", Status: "affected", Severity: 7.5m, RiskScore: riskScore, RiskSeverity: riskSeverity, RiskProfileVersion: "profile-1", RiskExplanationId: riskExplanationId, RiskEventSequence: 1, Labels: new JsonObject { ["k"] = "v" }, CurrentEventId: Guid.NewGuid(), ExplainRef: "ref", PolicyRationale: new JsonArray("r1"), UpdatedAt: DateTimeOffset.UtcNow, CycleHash: string.Empty); } }