work-flow/tests/Workflow.Tests/Form/FormDefinitionTests.cs

358 lines
13 KiB
C#

using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Workflow.Application.Form.DTOs;
using Workflow.Application.Form.FormDefinition.Commands;
using Workflow.Application.Form.FormDefinition.Queries;
using Workflow.Domain.Enums;
using Workflow.Domain.Exceptions;
using Workflow.Infrastructure.Persistence;
using Xunit;
namespace Workflow.Tests.Form;
[Collection("FormTests")]
public class FormDefinitionTests
{
private readonly FormTestFixture _fixture;
public FormDefinitionTests(FormTestFixtureClassFixture fixture)
{
_fixture = fixture;
}
private const string ValidSchema = """
{
"type": "object",
"properties": {
"name": {
"type": "string",
"title": "姓名",
"required": true,
"x-decorator": "FormItem",
"x-component": "Input"
}
}
}
""";
// ====================================================================
// CreateFormDefinition
// ====================================================================
[Fact]
public async Task CreateFormDefinition_WithValidSchema_ReturnsDto()
{
await using var db = _fixture.CreateDbContext();
var handler = new CreateFormDefinitionCommandHandler(db);
var command = new CreateFormDefinitionCommand(
Name: "员工入职登记表",
Code: "EMP_ONBOARD",
Description: "新员工入职时填写的登记表单",
SchemaJson: ValidSchema
);
var result = await handler.Handle(command, CancellationToken.None);
result.Should().NotBeNull();
result.Name.Should().Be("员工入职登记表");
result.Code.Should().Be("EMP_ONBOARD");
result.Version.Should().Be(1);
result.Status.Should().Be(FormStatus.Draft);
var entity = await db.FormDefinitions.FirstAsync();
entity.SchemaJson.Should().Contain("name");
}
[Fact]
public async Task CreateFormDefinition_WithDuplicateCode_ThrowsBusinessException()
{
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(CreateFormDefinition_WithDuplicateCode_ThrowsBusinessException),
seedAction: db =>
{
db.FormDefinitions.Add(new Domain.Entities.FormDefinition
{
Id = Guid.NewGuid(),
Name = "已存在的表单",
Code = "DUPLICATE_CODE",
Version = 1,
Status = FormStatus.Draft,
SchemaJson = ValidSchema,
});
});
var handler = new CreateFormDefinitionCommandHandler(db);
var command = new CreateFormDefinitionCommand(
Name: "新表单",
Code: "DUPLICATE_CODE",
Description: null,
SchemaJson: ValidSchema
);
var act = () => handler.Handle(command, CancellationToken.None);
await act.Should().ThrowAsync<BusinessException>()
.WithMessage("*编码*已存在*");
}
[Fact]
public async Task CreateFormDefinition_WithInvalidSchema_ThrowsBusinessException()
{
await using var db = _fixture.CreateDbContext();
var handler = new CreateFormDefinitionCommandHandler(db);
var command = new CreateFormDefinitionCommand(
Name: "无效表单",
Code: "INVALID_SCHEMA",
Description: null,
SchemaJson: """{ "type": "string" }"""
);
var act = () => handler.Handle(command, CancellationToken.None);
await act.Should().ThrowAsync<BusinessException>()
.WithMessage("*Schema 校验失败*");
}
// ====================================================================
// UpdateFormDefinition
// ====================================================================
[Fact]
public async Task UpdateFormDefinition_UpdatesNameAndIncrementsVersion()
{
var formId = Guid.NewGuid();
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(UpdateFormDefinition_UpdatesNameAndIncrementsVersion),
seedAction: db =>
{
db.FormDefinitions.Add(new Domain.Entities.FormDefinition
{
Id = formId,
Name = "旧名称",
Code = "FORM_001",
Version = 1,
Description = "旧描述",
Status = FormStatus.Draft,
SchemaJson = ValidSchema,
});
});
var handler = new UpdateFormDefinitionCommandHandler(db);
var command = new UpdateFormDefinitionCommand(
Id: formId,
Name: "新名称",
Description: "新描述",
SchemaJson: ValidSchema
);
var result = await handler.Handle(command, CancellationToken.None);
result.Should().NotBeNull();
result.Name.Should().Be("新名称");
result.Version.Should().Be(2);
var entity = await db.FormDefinitions.FindAsync(formId);
entity.Should().NotBeNull();
entity!.Version.Should().Be(2);
entity.Code.Should().Be("FORM_001");
}
// ====================================================================
// DeleteFormDefinition (soft delete)
// ====================================================================
[Fact]
public async Task DeleteFormDefinition_SetsIsDeletedTrue()
{
var formId = Guid.NewGuid();
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(DeleteFormDefinition_SetsIsDeletedTrue),
seedAction: ctx =>
{
ctx.FormDefinitions.Add(new Domain.Entities.FormDefinition
{
Id = formId,
Name = "待删除表单",
Code = "DEL_FORM",
Version = 1,
Status = FormStatus.Draft,
SchemaJson = ValidSchema,
});
});
var handler = new DeleteFormDefinitionCommandHandler(db);
await handler.Handle(new DeleteFormDefinitionCommand(Id: formId), CancellationToken.None);
var normalQuery = await db.FormDefinitions.FirstOrDefaultAsync(f => f.Id == formId);
normalQuery.Should().BeNull();
var deletedEntity = await db.FormDefinitions
.IgnoreQueryFilters()
.FirstAsync(f => f.Id == formId);
deletedEntity.IsDeleted.Should().BeTrue();
}
// ====================================================================
// PublishFormDefinition
// ====================================================================
[Fact]
public async Task PublishFormDefinition_ChangesStatusFromDraftToPublished()
{
var formId = Guid.NewGuid();
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(PublishFormDefinition_ChangesStatusFromDraftToPublished),
seedAction: ctx =>
{
ctx.FormDefinitions.Add(new Domain.Entities.FormDefinition
{
Id = formId,
Name = "待发布表单",
Code = "PUB_FORM",
Version = 1,
Status = FormStatus.Draft,
SchemaJson = ValidSchema,
});
});
var handler = new PublishFormDefinitionCommandHandler(db);
await handler.Handle(new PublishFormDefinitionCommand(Id: formId), CancellationToken.None);
var entity = await db.FormDefinitions.FindAsync(formId);
entity.Should().NotBeNull();
entity!.Status.Should().Be(FormStatus.Published);
}
[Fact]
public async Task PublishFormDefinition_AlreadyPublished_ThrowsBusinessException()
{
var formId = Guid.NewGuid();
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(PublishFormDefinition_AlreadyPublished_ThrowsBusinessException),
seedAction: ctx =>
{
ctx.FormDefinitions.Add(new Domain.Entities.FormDefinition
{
Id = formId,
Name = "已发布表单",
Code = "PUB_AGAIN",
Version = 1,
Status = FormStatus.Published,
SchemaJson = ValidSchema,
});
});
var handler = new PublishFormDefinitionCommandHandler(db);
var act = () => handler.Handle(new PublishFormDefinitionCommand(Id: formId), CancellationToken.None);
await act.Should().ThrowAsync<BusinessException>()
.WithMessage("*已发布*");
}
// ====================================================================
// DisableFormDefinition
// ====================================================================
[Fact]
public async Task DisableFormDefinition_ChangesStatusToDisabled()
{
var formId = Guid.NewGuid();
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(DisableFormDefinition_ChangesStatusToDisabled),
seedAction: ctx =>
{
ctx.FormDefinitions.Add(new Domain.Entities.FormDefinition
{
Id = formId,
Name = "待禁用表单",
Code = "DIS_FORM",
Version = 1,
Status = FormStatus.Published,
SchemaJson = ValidSchema,
});
});
var handler = new DisableFormDefinitionCommandHandler(db);
await handler.Handle(new DisableFormDefinitionCommand(Id: formId), CancellationToken.None);
var entity = await db.FormDefinitions.FindAsync(formId);
entity.Should().NotBeNull();
entity!.Status.Should().Be(FormStatus.Disabled);
}
// ====================================================================
// GetFormDefinitionList
// ====================================================================
[Fact]
public async Task GetFormDefinitionList_ReturnsPagedResults()
{
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(GetFormDefinitionList_ReturnsPagedResults),
seedAction: ctx =>
{
for (int i = 1; i <= 15; i++)
{
ctx.FormDefinitions.Add(new Domain.Entities.FormDefinition
{
Id = Guid.NewGuid(),
Name = $"表单_{i:D3}",
Code = $"FORM_{i:D3}",
Version = 1,
Status = FormStatus.Draft,
SchemaJson = ValidSchema,
});
}
});
var handler = new GetFormDefinitionListQueryHandler(db);
var result = await handler.Handle(
new GetFormDefinitionListQuery(PageIndex: 1, PageSize: 10, Status: null),
CancellationToken.None);
result.Items.Should().HaveCount(10);
result.TotalCount.Should().Be(15);
}
// ====================================================================
// GetFormDefinitionById
// ====================================================================
[Fact]
public async Task GetFormDefinitionById_ReturnsSchemaJson()
{
var formId = Guid.NewGuid();
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(GetFormDefinitionById_ReturnsSchemaJson),
seedAction: ctx =>
{
ctx.FormDefinitions.Add(new Domain.Entities.FormDefinition
{
Id = formId,
Name = "带 Schema 的表单",
Code = "WITH_SCHEMA",
Version = 1,
Description = "包含 Formily Schema 的表单",
Status = FormStatus.Draft,
SchemaJson = ValidSchema,
});
});
var handler = new GetFormDefinitionByIdQueryHandler(db);
var result = await handler.Handle(new GetFormDefinitionByIdQuery(Id: formId), CancellationToken.None);
result.Should().NotBeNull();
result.SchemaJson.Should().NotBeNullOrEmpty();
result.SchemaJson.Should().Contain("name");
}
[Fact]
public async Task GetFormDefinitionById_NotFound_ThrowsNotFoundException()
{
await using var db = _fixture.CreateDbContext(testName: nameof(GetFormDefinitionById_NotFound_ThrowsNotFoundException));
var handler = new GetFormDefinitionByIdQueryHandler(db);
var act = () => handler.Handle(new GetFormDefinitionByIdQuery(Id: Guid.NewGuid()), CancellationToken.None);
await act.Should().ThrowAsync<NotFoundException>()
.WithMessage("*表单*不存在*");
}
}