215 lines
8.0 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Workflow.Application.Form.FormData.Commands;
using Workflow.Application.Form.FormData.Queries;
using Workflow.Domain.Enums;
using Workflow.Domain.Exceptions;
using Workflow.Infrastructure.Persistence;
using Xunit;
namespace Workflow.Tests.Form;
[Collection("FormTests")]
public class FormDataTests
{
private readonly FormTestFixture _fixture;
public FormDataTests(FormTestFixtureClassFixture fixture)
{
_fixture = fixture;
}
/// <summary>
/// 辅助方法:创建一个已发布的表单定义(含 Formily Schema用于 FormData 测试。
/// Schema 包含name (string, required)、age (number)、gender (string, required)、resume (array)。
/// </summary>
private static Domain.Entities.FormDefinition CreatePublishedFormDefinition(Guid formId)
{
return new Domain.Entities.FormDefinition
{
Id = formId,
Name = "测试表单",
Code = $"TEST_FORM_{formId:N}",
Version = 1,
Status = FormStatus.Published,
SchemaJson = """
{
"type": "object",
"properties": {
"name": {
"type": "string",
"title": "姓名",
"required": true,
"x-decorator": "FormItem",
"x-component": "Input"
},
"age": {
"type": "number",
"title": "年龄",
"x-decorator": "FormItem",
"x-component": "InputNumber",
"x-component-props": { "min": 0, "max": 150 }
},
"gender": {
"type": "string",
"title": "性别",
"required": true,
"enum": [
{ "label": "男", "value": "male" },
{ "label": "女", "value": "female" }
],
"x-decorator": "FormItem",
"x-component": "Radio.Group"
},
"resume": {
"type": "array",
"title": "简历附件",
"x-decorator": "FormItem",
"x-component": "Upload",
"x-component-props": { "maxSize": 10485760, "accept": ".pdf,.doc,.docx" }
}
}
}
""",
};
}
// ====================================================================
// SubmitFormData
// ====================================================================
[Fact]
public async Task SubmitFormData_StoresDataJson()
{
var formId = Guid.NewGuid();
var instanceId = Guid.NewGuid();
var dataJson = """{"name":"","age":25,"gender":"male"}""";
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(SubmitFormData_StoresDataJson),
seedAction: ctx => ctx.FormDefinitions.Add(CreatePublishedFormDefinition(formId)));
var handler = new SubmitFormDataCommandHandler(db);
var formDataId = await handler.Handle(
new SubmitFormDataCommand(FormDefinitionId: formId, InstanceId: instanceId, DataJson: dataJson),
CancellationToken.None);
formDataId.Should().NotBe(Guid.Empty);
var savedData = await db.FormData.FindAsync(formDataId);
savedData.Should().NotBeNull();
savedData!.DataJson.Should().Be(dataJson);
}
[Fact]
public async Task SubmitFormData_AssociatesWithFormDefinitionAndInstance()
{
var formId = Guid.NewGuid();
var instanceId = Guid.NewGuid();
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(SubmitFormData_AssociatesWithFormDefinitionAndInstance),
seedAction: ctx => ctx.FormDefinitions.Add(CreatePublishedFormDefinition(formId)));
var handler = new SubmitFormDataCommandHandler(db);
var formDataId = await handler.Handle(
new SubmitFormDataCommand(FormDefinitionId: formId, InstanceId: instanceId, DataJson: """{"name":"","age":30,"gender":"female"}"""),
CancellationToken.None);
var savedData = await db.FormData.FindAsync(formDataId);
savedData.Should().NotBeNull();
savedData!.FormDefinitionId.Should().Be(formId);
savedData.InstanceId.Should().Be(instanceId);
}
[Fact]
public async Task SubmitFormData_RequiredFieldMissing_ThrowsBusinessException()
{
var formId = Guid.NewGuid();
var instanceId = Guid.NewGuid();
var dataJson = """{"age":25,"gender":"male"}""";
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(SubmitFormData_RequiredFieldMissing_ThrowsBusinessException),
seedAction: ctx => ctx.FormDefinitions.Add(CreatePublishedFormDefinition(formId)));
var handler = new SubmitFormDataCommandHandler(db);
var act = () => handler.Handle(
new SubmitFormDataCommand(FormDefinitionId: formId, InstanceId: instanceId, DataJson: dataJson),
CancellationToken.None);
await act.Should().ThrowAsync<BusinessException>()
.WithMessage("*必填*姓名*");
var count = await db.FormData.CountAsync();
count.Should().Be(0);
}
[Fact]
public async Task SubmitFormData_InvalidNumberType_ThrowsBusinessException()
{
var formId = Guid.NewGuid();
var instanceId = Guid.NewGuid();
var dataJson = """{"name":"","age":"abc","gender":"male"}""";
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(SubmitFormData_InvalidNumberType_ThrowsBusinessException),
seedAction: ctx => ctx.FormDefinitions.Add(CreatePublishedFormDefinition(formId)));
var handler = new SubmitFormDataCommandHandler(db);
var act = () => handler.Handle(
new SubmitFormDataCommand(FormDefinitionId: formId, InstanceId: instanceId, DataJson: dataJson),
CancellationToken.None);
await act.Should().ThrowAsync<BusinessException>()
.WithMessage("*age*类型*");
var count = await db.FormData.CountAsync();
count.Should().Be(0);
}
// ====================================================================
// GetFormDataByInstance
// ====================================================================
[Fact]
public async Task GetFormDataByInstance_ReturnsFormData()
{
var formId = Guid.NewGuid();
var instanceId = Guid.NewGuid();
var formDataId = Guid.NewGuid();
var dataJson = """{"name":"","age":35,"gender":"male"}""";
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(GetFormDataByInstance_ReturnsFormData),
seedAction: ctx =>
{
ctx.FormDefinitions.Add(CreatePublishedFormDefinition(formId));
ctx.FormData.Add(new Domain.Entities.FormData
{
Id = formDataId,
FormDefinitionId = formId,
InstanceId = instanceId,
DataJson = dataJson,
});
});
var handler = new GetFormDataByInstanceQueryHandler(db);
var result = await handler.Handle(new GetFormDataByInstanceQuery(InstanceId: instanceId), CancellationToken.None);
result.Should().NotBeNull();
result!.Id.Should().Be(formDataId);
result.DataJson.Should().Be(dataJson);
}
[Fact]
public async Task GetFormDataByInstance_NotFound_ReturnsNull()
{
await using var db = _fixture.CreateDbContext(testName: nameof(GetFormDataByInstance_NotFound_ReturnsNull));
var handler = new GetFormDataByInstanceQueryHandler(db);
var result = await handler.Handle(new GetFormDataByInstanceQuery(InstanceId: Guid.NewGuid()), CancellationToken.None);
result.Should().BeNull();
}
}