using System.Text.Json; using StellaOps.Policy.RiskProfile.Validation; using Xunit; using StellaOps.TestKit; namespace StellaOps.Policy.RiskProfile.Tests; public class RiskProfileValidatorTests { private readonly RiskProfileValidator _validator = new(); [Trait("Category", TestCategories.Unit)] [Fact] public void Valid_profile_passes_schema() { var profile = """ { "id": "default-risk", "version": "1.0.0", "description": "Baseline risk profile", "signals": [ { "name": "reachability", "source": "signals", "type": "boolean", "path": "/reachability/exploitable" }, { "name": "kev", "source": "cisa", "type": "boolean" } ], "weights": { "reachability": 0.6, "kev": 0.4 }, "overrides": { "severity": [ { "when": { "kev": true }, "set": "critical" } ], "decisions": [ { "when": { "reachability": false }, "action": "review", "reason": "Not reachable" } ] }, "metadata": { "owner": "risk-team" } } """; var result = _validator.Validate(profile); Assert.True(result.IsValid, string.Join(" | ", result.Errors ?? Array.Empty())); } [Trait("Category", TestCategories.Unit)] [Fact] public void Missing_required_fields_fails_schema() { var invalidProfile = """ { "id": "missing-fields" } """; var result = _validator.Validate(invalidProfile); Assert.False(result.IsValid); Assert.NotEmpty(result.Errors); } [Trait("Category", TestCategories.Unit)] [Fact] public void Empty_payload_throws() { Assert.Throws(() => _validator.Validate(" ")); } }