- Add FormDefinitionVersion with compare/versions endpoints and schema differ - Add Notification entity, endpoints and application features - Add Scheduler (timeout) and WebhookDispatcher services - Add FormDataValidator/FieldPermissionEvaluator/ReactionEvaluator - Add workflow task mark-read, CC support and SystemUserContext - Add EF migrations for form versions and notifications - Add unit tests for form schema, notifications, scheduler and serialization
91 lines
3.4 KiB
C#
91 lines
3.4 KiB
C#
using System.Text.Json;
|
||
using FluentAssertions;
|
||
using Workflow.Api.Serialization;
|
||
using Xunit;
|
||
|
||
namespace Workflow.Tests.Serialization;
|
||
|
||
/// <summary>
|
||
/// 验证统一数据规范:DateTime / DateTimeOffset 序列化为 UTC 毫秒时间戳(long)。
|
||
/// 这是第二期"后端数据规范统一"的核心契约 —— 后端只输出毫秒时间戳,时区/格式化交给前端。
|
||
/// </summary>
|
||
public class TimestampJsonConverterTests
|
||
{
|
||
private static JsonSerializerOptions BuildOptions() => new()
|
||
{
|
||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||
Converters = { new TimestampDateTimeConverter(), new TimestampDateTimeOffsetConverter() }
|
||
};
|
||
|
||
private class SampleDto
|
||
{
|
||
public Guid Id { get; set; }
|
||
public DateTime CreatedAt { get; set; }
|
||
public DateTimeOffset? UpdatedAt { get; set; }
|
||
}
|
||
|
||
[Fact]
|
||
public void DateTime_serializes_to_millisecond_epoch_number()
|
||
{
|
||
// 固定时刻:2026-06-14T03:30:00Z
|
||
var utc = new DateTime(2026, 6, 14, 3, 30, 0, DateTimeKind.Utc);
|
||
var expectedMs = new DateTimeOffset(utc, TimeSpan.Zero).ToUnixTimeMilliseconds();
|
||
|
||
var dto = new SampleDto { Id = Guid.NewGuid(), CreatedAt = utc };
|
||
var json = JsonSerializer.Serialize(dto, BuildOptions());
|
||
|
||
using var doc = JsonDocument.Parse(json);
|
||
doc.RootElement.GetProperty("createdAt").ValueKind.Should().Be(JsonValueKind.Number);
|
||
doc.RootElement.GetProperty("createdAt").GetInt64().Should().Be(expectedMs);
|
||
}
|
||
|
||
[Fact]
|
||
public void DateTimeOffset_serializes_to_millisecond_epoch_number()
|
||
{
|
||
var dto = new DateTimeOffset(2026, 1, 2, 3, 4, 5, TimeSpan.Zero);
|
||
var expectedMs = dto.ToUnixTimeMilliseconds();
|
||
|
||
var json = JsonSerializer.Serialize(new { t = dto }, BuildOptions());
|
||
|
||
using var doc = JsonDocument.Parse(json);
|
||
doc.RootElement.GetProperty("t").GetInt64().Should().Be(expectedMs);
|
||
}
|
||
|
||
[Fact]
|
||
public void Guid_serializes_as_string()
|
||
{
|
||
// 验证 ID 统一为字符串(Guid 天然序列化为字符串,无需额外转换器)
|
||
var id = Guid.NewGuid();
|
||
var dto = new SampleDto { Id = id };
|
||
var json = JsonSerializer.Serialize(dto, BuildOptions());
|
||
|
||
using var doc = JsonDocument.Parse(json);
|
||
doc.RootElement.GetProperty("id").ValueKind.Should().Be(JsonValueKind.String);
|
||
doc.RootElement.GetProperty("id").GetGuid().Should().Be(id);
|
||
}
|
||
|
||
[Fact]
|
||
public void Deserializes_epoch_number_back_to_utc_dateTime()
|
||
{
|
||
var utc = new DateTime(2026, 6, 14, 3, 30, 0, DateTimeKind.Utc);
|
||
var ms = new DateTimeOffset(utc, TimeSpan.Zero).ToUnixTimeMilliseconds();
|
||
var json = $$"""{"id":"00000000-0000-0000-0000-000000000000","createdAt":{{ms}}}""";
|
||
|
||
var dto = JsonSerializer.Deserialize<SampleDto>(json, BuildOptions())!;
|
||
|
||
dto.CreatedAt.Should().Be(utc);
|
||
dto.CreatedAt.Kind.Should().Be(DateTimeKind.Utc);
|
||
}
|
||
|
||
[Fact]
|
||
public void Deserializes_legacy_iso_string_for_backward_compatibility()
|
||
{
|
||
// 旧客户端/旧数据可能仍是 ISO 字符串,必须能读回(向后兼容)
|
||
var json = """{"id":"00000000-0000-0000-0000-000000000000","createdAt":"2026-06-14T03:30:00Z"}""";
|
||
|
||
var dto = JsonSerializer.Deserialize<SampleDto>(json, BuildOptions())!;
|
||
|
||
dto.CreatedAt.Should().Be(new DateTime(2026, 6, 14, 3, 30, 0, DateTimeKind.Utc));
|
||
}
|
||
}
|