work-flow/tests/Workflow.Tests/Form/SchemaDifferTests.cs
向宁 9f878286e7 feat: form versioning, notification center, scheduler and webhooks
- 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
2026-06-14 15:03:11 +08:00

167 lines
4.8 KiB
C#

using FluentAssertions;
using Workflow.Application.Form.Schema;
using Xunit;
namespace Workflow.Tests.Form;
public class SchemaDifferTests
{
private const string OldSchema = """
{
"type": "object",
"properties": {
"name": { "type": "string", "title": "姓名", "required": true, "x-component": "Input" },
"age": { "type": "number", "title": "年龄", "x-component": "InputNumber" },
"gender": { "type": "string", "title": "性别", "x-component": "Select" }
}
}
""";
[Fact]
public void Diff_NoChanges_ReturnsEmpty()
{
var diff = SchemaDiffer.Diff(OldSchema, OldSchema);
diff.Added.Should().BeEmpty();
diff.Removed.Should().BeEmpty();
diff.Modified.Should().BeEmpty();
}
[Fact]
public void Diff_DetectsAddedField()
{
var newSchema = """
{
"type": "object",
"properties": {
"name": { "type": "string", "title": "姓名", "required": true, "x-component": "Input" },
"age": { "type": "number", "title": "年龄", "x-component": "InputNumber" },
"gender": { "type": "string", "title": "性别", "x-component": "Select" },
"email": { "type": "string", "title": "邮箱", "x-component": "Input" }
}
}
""";
var diff = SchemaDiffer.Diff(OldSchema, newSchema);
diff.Added.Should().ContainSingle(f => f.Path == "email");
diff.Removed.Should().BeEmpty();
diff.Modified.Should().BeEmpty();
}
[Fact]
public void Diff_DetectsRemovedField()
{
var newSchema = """
{
"type": "object",
"properties": {
"name": { "type": "string", "title": "姓名", "required": true, "x-component": "Input" },
"age": { "type": "number", "title": "年龄", "x-component": "InputNumber" }
}
}
""";
var diff = SchemaDiffer.Diff(OldSchema, newSchema);
diff.Removed.Should().ContainSingle(f => f.Path == "gender");
diff.Added.Should().BeEmpty();
}
[Fact]
public void Diff_DetectsComponentChange()
{
var newSchema = """
{
"type": "object",
"properties": {
"name": { "type": "string", "title": "姓名", "required": true, "x-component": "Input" },
"age": { "type": "number", "title": "年龄", "x-component": "Input" },
"gender": { "type": "string", "title": "性别", "x-component": "Select" }
}
}
""";
var diff = SchemaDiffer.Diff(OldSchema, newSchema);
diff.Modified.Should().ContainSingle(f => f.Path == "age" && f.Change!.Contains("组件"));
}
[Fact]
public void Diff_DetectsRequiredChange()
{
var newSchema = """
{
"type": "object",
"properties": {
"name": { "type": "string", "title": "姓名", "x-component": "Input" },
"age": { "type": "number", "title": "年龄", "x-component": "InputNumber" },
"gender": { "type": "string", "title": "性别", "x-component": "Select" }
}
}
""";
var diff = SchemaDiffer.Diff(OldSchema, newSchema);
diff.Modified.Should().ContainSingle(f => f.Path == "name" && f.Change!.Contains("必填"));
}
[Fact]
public void Diff_HandlesNullInputs()
{
var diff = SchemaDiffer.Diff(null, null);
diff.Added.Should().BeEmpty();
diff.Removed.Should().BeEmpty();
diff.Modified.Should().BeEmpty();
}
[Fact]
public void Diff_HandlesInvalidJson()
{
var diff = SchemaDiffer.Diff("not json", "{ }");
// 无效的旧 schema 视为空,新 schema 也空,无差异
diff.Added.Should().BeEmpty();
diff.Removed.Should().BeEmpty();
}
[Fact]
public void Diff_HandlesNestedFields()
{
var oldNested = """
{
"type": "object",
"properties": {
"card": {
"type": "void",
"x-component": "Card",
"properties": {
"name": { "type": "string", "title": "姓名", "x-component": "Input" }
}
}
}
}
""";
var newNested = """
{
"type": "object",
"properties": {
"card": {
"type": "void",
"x-component": "Card",
"properties": {
"name": { "type": "string", "title": "姓名", "x-component": "Input" },
"age": { "type": "number", "title": "年龄", "x-component": "InputNumber" }
}
}
}
}
""";
var diff = SchemaDiffer.Diff(oldNested, newNested);
diff.Added.Should().ContainSingle(f => f.Path == "card.age");
}
}