358 lines
13 KiB
C#
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("*表单*不存在*");
|
|
}
|
|
}
|