using System.Text.Json; namespace ScadaLink.InboundAPI.Tests; /// /// WP-2: Tests for parameter validation — type checking, required fields, extended type system. /// public class ParameterValidatorTests { [Fact] public void NoDefinitions_NoBody_ReturnsValid() { var result = ParameterValidator.Validate(null, null); Assert.True(result.IsValid); Assert.Empty(result.Parameters); } [Fact] public void EmptyDefinitions_ReturnsValid() { var result = ParameterValidator.Validate(null, "[]"); Assert.True(result.IsValid); } [Fact] public void RequiredParameterMissing_ReturnsInvalid() { var definitions = JsonSerializer.Serialize(new[] { new { Name = "value", Type = "Integer", Required = true } }); var result = ParameterValidator.Validate(null, definitions); Assert.False(result.IsValid); Assert.Contains("Missing required parameter", result.ErrorMessage); } [Fact] public void BodyNotObject_ReturnsInvalid() { var definitions = JsonSerializer.Serialize(new[] { new { Name = "value", Type = "String", Required = true } }); using var doc = JsonDocument.Parse("\"just a string\""); var result = ParameterValidator.Validate(doc.RootElement.Clone(), definitions); Assert.False(result.IsValid); Assert.Contains("must be a JSON object", result.ErrorMessage); } [Theory] [InlineData("Boolean", "true", true)] [InlineData("Integer", "42", (long)42)] [InlineData("Float", "3.14", 3.14)] [InlineData("String", "\"hello\"", "hello")] public void ValidTypeCoercion_Succeeds(string type, string jsonValue, object expected) { var definitions = JsonSerializer.Serialize(new[] { new { Name = "val", Type = type, Required = true } }); using var doc = JsonDocument.Parse($"{{\"val\": {jsonValue}}}"); var result = ParameterValidator.Validate(doc.RootElement.Clone(), definitions); Assert.True(result.IsValid); Assert.Equal(expected, result.Parameters["val"]); } [Fact] public void WrongType_ReturnsInvalid() { var definitions = JsonSerializer.Serialize(new[] { new { Name = "count", Type = "Integer", Required = true } }); using var doc = JsonDocument.Parse("{\"count\": \"not a number\"}"); var result = ParameterValidator.Validate(doc.RootElement.Clone(), definitions); Assert.False(result.IsValid); Assert.Contains("must be an Integer", result.ErrorMessage); } [Fact] public void ObjectType_Parsed() { var definitions = JsonSerializer.Serialize(new[] { new { Name = "data", Type = "Object", Required = true } }); using var doc = JsonDocument.Parse("{\"data\": {\"key\": \"value\"}}"); var result = ParameterValidator.Validate(doc.RootElement.Clone(), definitions); Assert.True(result.IsValid); Assert.IsType>(result.Parameters["data"]); } [Fact] public void ListType_Parsed() { var definitions = JsonSerializer.Serialize(new[] { new { Name = "items", Type = "List", Required = true } }); using var doc = JsonDocument.Parse("{\"items\": [1, 2, 3]}"); var result = ParameterValidator.Validate(doc.RootElement.Clone(), definitions); Assert.True(result.IsValid); Assert.IsType>(result.Parameters["items"]); } [Fact] public void OptionalParameter_MissingBody_ReturnsValid() { var definitions = JsonSerializer.Serialize(new[] { new { Name = "optional", Type = "String", Required = false } }); var result = ParameterValidator.Validate(null, definitions); Assert.True(result.IsValid); } [Fact] public void UnknownType_ReturnsInvalid() { var definitions = JsonSerializer.Serialize(new[] { new { Name = "val", Type = "CustomType", Required = true } }); using var doc = JsonDocument.Parse("{\"val\": \"test\"}"); var result = ParameterValidator.Validate(doc.RootElement.Clone(), definitions); Assert.False(result.IsValid); Assert.Contains("Unknown parameter type", result.ErrorMessage); } }