Files
scadalink-design/tests/ScadaLink.InboundAPI.Tests/ReturnValueValidatorTests.cs

119 lines
3.5 KiB
C#

namespace ScadaLink.InboundAPI.Tests;
/// <summary>
/// InboundAPI-014: tests for return-value validation against a method's
/// <c>ReturnDefinition</c>. Previously the script's return value was serialized
/// verbatim with no checking against the declared return structure.
/// </summary>
public class ReturnValueValidatorTests
{
// --- No definition → no validation (backward compatible) ---
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void NoReturnDefinition_AnythingIsValid(string? returnDefinition)
{
var result = ReturnValueValidator.Validate("{\"anything\":1}", returnDefinition);
Assert.True(result.IsValid);
}
[Fact]
public void NoReturnDefinition_NullResult_IsValid()
{
var result = ReturnValueValidator.Validate(null, null);
Assert.True(result.IsValid);
}
// --- Happy path: result matches the declared field shape ---
[Fact]
public void ResultMatchingDefinition_IsValid()
{
const string def = """[{"name":"siteName","type":"String"},{"name":"totalUnits","type":"Integer"}]""";
const string json = """{"siteName":"Site Alpha","totalUnits":14250}""";
var result = ReturnValueValidator.Validate(json, def);
Assert.True(result.IsValid);
}
[Fact]
public void ResultWithListField_ShapeChecked_IsValid()
{
const string def = """[{"name":"lines","type":"List"}]""";
const string json = """{"lines":[{"lineName":"Line-1","units":8200}]}""";
var result = ReturnValueValidator.Validate(json, def);
Assert.True(result.IsValid);
}
// --- Mismatches must be reported ---
[Fact]
public void ResultMissingDeclaredField_IsInvalid()
{
const string def = """[{"name":"siteName","type":"String"},{"name":"totalUnits","type":"Integer"}]""";
const string json = """{"siteName":"Site Alpha"}""";
var result = ReturnValueValidator.Validate(json, def);
Assert.False(result.IsValid);
Assert.Contains("totalUnits", result.ErrorMessage);
}
[Fact]
public void ResultFieldWrongType_IsInvalid()
{
const string def = """[{"name":"totalUnits","type":"Integer"}]""";
const string json = """{"totalUnits":"not-a-number"}""";
var result = ReturnValueValidator.Validate(json, def);
Assert.False(result.IsValid);
Assert.Contains("totalUnits", result.ErrorMessage);
}
[Fact]
public void NullResultWhenStructureRequired_IsInvalid()
{
const string def = """[{"name":"siteName","type":"String"}]""";
var result = ReturnValueValidator.Validate(null, def);
Assert.False(result.IsValid);
}
[Fact]
public void NonObjectResultWhenStructureRequired_IsInvalid()
{
const string def = """[{"name":"siteName","type":"String"}]""";
var result = ReturnValueValidator.Validate("42", def);
Assert.False(result.IsValid);
}
[Fact]
public void ListFieldGivenNonArray_IsInvalid()
{
const string def = """[{"name":"lines","type":"List"}]""";
const string json = """{"lines":"not-a-list"}""";
var result = ReturnValueValidator.Validate(json, def);
Assert.False(result.IsValid);
Assert.Contains("lines", result.ErrorMessage);
}
[Fact]
public void MalformedReturnDefinition_IsInvalid()
{
var result = ReturnValueValidator.Validate("{\"x\":1}", "%%% not json %%%");
Assert.False(result.IsValid);
}
}