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);
}
}