using System.Text.Json;
using FluentAssertions;
using Scripting.Application.Services;
using Xunit;
namespace BuildFlow.Scripting.Tests;
///
/// ScriptExecutionService.ParseVariables 类型映射逻辑测试(纯函数,无需 DI/数据库)。
/// 验证 JSON 输入被正确转为原生 .NET 类型,让脚本能直接 (decimal)Variables["x"] 而非操作 JsonElement。
///
public class ScriptExecutionServiceParseVariablesTests
{
// ParseVariables 是 internal static,通过 InternalsVisibleTo 暴露给本测试程序集
private static IReadOnlyDictionary Parse(string json)
=> ScriptExecutionService.ParseVariables(json);
[Fact]
public void Empty_Or_Whitespace_Input_Returns_Empty_Dictionary()
{
Parse("").Should().BeEmpty();
Parse(" ").Should().BeEmpty();
}
[Fact]
public void Integer_Number_Maps_To_Long()
{
var vars = Parse("{\"count\": 42, \"big\": 9007199254740993}");
vars["count"].Should().BeOfType().Which.Should().Be(42L);
// 9007199254740993 超出 double 精度但 long 可表示,应保持 long
vars["big"].Should().BeOfType().Which.Should().Be(9007199254740993L);
}
[Fact]
public void Decimal_Number_Maps_To_Decimal_For_Money_Friendliness()
{
var vars = Parse("{\"price\": 19.99}");
vars["price"].Should().BeOfType().Which.Should().Be(19.99m);
}
[Fact]
public void Bool_Maps_To_Boolean()
{
var vars = Parse("{\"active\": true, \"deleted\": false}");
vars["active"].Should().BeOfType().Which.Should().BeTrue();
vars["deleted"].Should().BeOfType().Which.Should().BeFalse();
}
[Fact]
public void Null_Maps_To_Null()
{
var vars = Parse("{\"name\": null}");
vars["name"].Should().BeNull();
}
[Fact]
public void Plain_String_Maps_To_String()
{
var vars = Parse("{\"title\": \"hello world\"}");
vars["title"].Should().BeOfType().Which.Should().Be("hello world");
}
[Fact]
public void Iso8601_String_Maps_To_DateTime()
{
var vars = Parse("{\"createdAt\": \"2026-07-06T10:30:00Z\"}");
vars["createdAt"].Should().BeOfType();
var dt = (DateTime)vars["createdAt"]!;
dt.Year.Should().Be(2026);
dt.Month.Should().Be(7);
dt.Day.Should().Be(6);
dt.Hour.Should().Be(10);
dt.Minute.Should().Be(30);
}
[Fact]
public void Guid_String_Maps_To_Guid()
{
var guidStr = "f47ac10b-58cc-4372-a567-0e02b2c3d479";
var vars = Parse("{\"id\": \"" + guidStr + "\"}");
vars["id"].Should().BeOfType();
((Guid)vars["id"]!).Should().Be(Guid.Parse(guidStr));
}
/// ISO 8601 日期优先于 Guid(日期串不会误判为 Guid)。
[Fact]
public void Date_Parsed_Before_Guid_Check()
{
var vars = Parse("{\"ts\": \"2026-01-15\"}");
vars["ts"].Should().BeOfType();
}
[Fact]
public void Object_Maps_To_JsonElement()
{
var vars = Parse("{\"nested\": {\"a\": 1, \"b\": 2}}");
vars["nested"].Should().BeOfType();
var el = (JsonElement)vars["nested"]!;
el.ValueKind.Should().Be(JsonValueKind.Object);
}
[Fact]
public void Array_Maps_To_JsonElement()
{
var vars = Parse("{\"items\": [1, 2, 3]}");
vars["items"].Should().BeOfType();
var el = (JsonElement)vars["items"]!;
el.ValueKind.Should().Be(JsonValueKind.Array);
el.GetArrayLength().Should().Be(3);
}
[Fact]
public void Mixed_Types_All_Resolve_Correctly()
{
var json = "{\"count\": 100, \"price\": 49.95, \"active\": true, \"name\": \"widget\", "
+ "\"id\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"date\": \"2026-07-06T00:00:00\", "
+ "\"meta\": {\"key\": \"val\"}, \"nullable\": null}";
var vars = Parse(json);
vars["count"].Should().BeOfType();
vars["price"].Should().BeOfType();
vars["active"].Should().BeOfType();
vars["name"].Should().BeOfType();
vars["id"].Should().BeOfType();
vars["date"].Should().BeOfType();
vars["meta"].Should().BeOfType();
vars["nullable"].Should().BeNull();
}
/// 脚本端典型用法:原生类型可直接强转,无需 GetInt64() 等中间步骤。
[Fact]
public void Script_Idiom_Direct_Cast_Works()
{
var vars = Parse("{\"qty\": 5, \"rate\": 12.5}");
// 模拟脚本里写 (long)Variables["qty"] * (decimal)Variables["rate"]
var result = (long)vars["qty"]! * (decimal)vars["rate"]!;
result.Should().Be(62.5m);
}
/// Ordinal 键比较器:键名大小写敏感。
[Fact]
public void Keys_Are_Case_Sensitive_Ordinal()
{
var vars = Parse("{\"Name\": \"a\", \"name\": \"b\"}");
vars.Should().HaveCount(2);
vars["Name"].Should().Be("a");
vars["name"].Should().Be("b");
}
}