work-flow/tests/Workflow.Tests/Form/FormDataTests.cs
2026-05-17 22:46:19 +08:00

294 lines
9.9 KiB
C#
Raw 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.DTOs;
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>
/// 辅助方法:创建一个已发布的表单定义并返回其 Id
/// 用于 FormData 测试的前置条件。
/// </summary>
private static Domain.Entities.FormDefinition CreatePublishedFormDefinition(Guid formId)
{
var form = new Domain.Entities.FormDefinition
{
Id = formId,
Name = "测试表单",
Code = $"TEST_FORM_{formId:N}",
Version = 1,
Status = FormStatus.Published,
SchemaJson = "{}",
};
form.Fields.Add(new Domain.Entities.FormDefinitionField
{
Id = Guid.NewGuid(),
FormDefinitionId = formId,
FieldKey = "name",
Label = "姓名",
FieldType = FieldType.Input,
Required = true,
Config = "{}",
SortOrder = 0,
});
form.Fields.Add(new Domain.Entities.FormDefinitionField
{
Id = Guid.NewGuid(),
FormDefinitionId = formId,
FieldKey = "age",
Label = "年龄",
FieldType = FieldType.Number,
Required = false,
DefaultValue = "18",
Config = """{"min":0,"max":150}""",
SortOrder = 1,
});
form.Fields.Add(new Domain.Entities.FormDefinitionField
{
Id = Guid.NewGuid(),
FormDefinitionId = formId,
FieldKey = "gender",
Label = "性别",
FieldType = FieldType.Radio,
Required = true,
Config = """{"options":["",""]}""",
SortOrder = 2,
});
form.Fields.Add(new Domain.Entities.FormDefinitionField
{
Id = Guid.NewGuid(),
FormDefinitionId = formId,
FieldKey = "resume",
Label = "简历附件",
FieldType = FieldType.FileUpload,
Required = false,
Config = """{"maxSize":10485760,"extensions":[".pdf",".doc",".docx"]}""",
SortOrder = 3,
});
return form;
}
// ====================================================================
// SubmitFormData
// ====================================================================
[Fact]
public async Task SubmitFormData_StoresDataJson()
{
// Arrange
var formId = Guid.NewGuid();
var instanceId = Guid.NewGuid();
var dataJson = """{"name":"","age":25,"gender":""}""";
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(SubmitFormData_StoresDataJson),
seedAction: ctx => ctx.FormDefinitions.Add(CreatePublishedFormDefinition(formId)));
var handler = new SubmitFormDataCommandHandler(db);
var command = new SubmitFormDataCommand(
FormDefinitionId: formId,
InstanceId: instanceId,
DataJson: dataJson
);
// Act
var formDataId = await handler.Handle(command, CancellationToken.None);
// Assert
formDataId.Should().NotBe(Guid.Empty);
var savedData = await db.FormDatas.FindAsync(formDataId);
savedData.Should().NotBeNull();
savedData!.DataJson.Should().Be(dataJson);
}
[Fact]
public async Task SubmitFormData_AssociatesWithFormDefinition()
{
// Arrange
var formId = Guid.NewGuid();
var instanceId = Guid.NewGuid();
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(SubmitFormData_AssociatesWithFormDefinition),
seedAction: ctx => ctx.FormDefinitions.Add(CreatePublishedFormDefinition(formId)));
var handler = new SubmitFormDataCommandHandler(db);
var command = new SubmitFormDataCommand(
FormDefinitionId: formId,
InstanceId: instanceId,
DataJson: """{"name":"","age":30,"gender":""}"""
);
// Act
var formDataId = await handler.Handle(command, CancellationToken.None);
// Assert
var savedData = await db.FormDatas.FindAsync(formDataId);
savedData.Should().NotBeNull();
savedData!.FormDefinitionId.Should().Be(formId);
}
[Fact]
public async Task SubmitFormData_AssociatesWithWorkflowInstance()
{
// Arrange
var formId = Guid.NewGuid();
var instanceId = Guid.NewGuid();
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(SubmitFormData_AssociatesWithWorkflowInstance),
seedAction: ctx => ctx.FormDefinitions.Add(CreatePublishedFormDefinition(formId)));
var handler = new SubmitFormDataCommandHandler(db);
var command = new SubmitFormDataCommand(
FormDefinitionId: formId,
InstanceId: instanceId,
DataJson: """{"name":"","age":28,"gender":""}"""
);
// Act
var formDataId = await handler.Handle(command, CancellationToken.None);
// Assert
var savedData = await db.FormDatas.FindAsync(formDataId);
savedData.Should().NotBeNull();
savedData!.InstanceId.Should().Be(instanceId);
}
[Fact]
public async Task SubmitFormData_RequiredFieldMissing_ThrowsBusinessException()
{
// Arrange
var formId = Guid.NewGuid();
var instanceId = Guid.NewGuid();
// name 是必填字段,但 DataJson 中没有 name
var dataJson = """{"age":25,"gender":""}""";
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 command = new SubmitFormDataCommand(
FormDefinitionId: formId,
InstanceId: instanceId,
DataJson: dataJson
);
// Act
var act = () => handler.Handle(command, CancellationToken.None);
// Assert
await act.Should().ThrowAsync<BusinessException>()
.WithMessage("*必填*name*");
// 验证数据未被写入
var count = await db.FormDatas.CountAsync();
count.Should().Be(0);
}
[Fact]
public async Task SubmitFormData_InvalidFieldType_ThrowsBusinessException()
{
// Arrange
var formId = Guid.NewGuid();
var instanceId = Guid.NewGuid();
// age 字段类型为 Number但提交了非数字值 "abc"
var dataJson = """{"name":"","age":"abc","gender":""}""";
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(SubmitFormData_InvalidFieldType_ThrowsBusinessException),
seedAction: ctx => ctx.FormDefinitions.Add(CreatePublishedFormDefinition(formId)));
var handler = new SubmitFormDataCommandHandler(db);
var command = new SubmitFormDataCommand(
FormDefinitionId: formId,
InstanceId: instanceId,
DataJson: dataJson
);
// Act
var act = () => handler.Handle(command, CancellationToken.None);
// Assert
await act.Should().ThrowAsync<BusinessException>()
.WithMessage("*age*类型*");
// 验证数据未被写入
var count = await db.FormDatas.CountAsync();
count.Should().Be(0);
}
// ====================================================================
// GetFormDataByInstance
// ====================================================================
[Fact]
public async Task GetFormDataByInstance_ReturnsFormData()
{
// Arrange
var formId = Guid.NewGuid();
var instanceId = Guid.NewGuid();
var formDataId = Guid.NewGuid();
var dataJson = """{"name":"","age":35,"gender":""}""";
await using var db = await _fixture.CreateDbContextWithSeedAsync(
testName: nameof(GetFormDataByInstance_ReturnsFormData),
seedAction: ctx =>
{
ctx.FormDefinitions.Add(CreatePublishedFormDefinition(formId));
ctx.FormDatas.Add(new Domain.Entities.FormData
{
Id = formDataId,
FormDefinitionId = formId,
InstanceId = instanceId,
DataJson = dataJson,
});
});
var handler = new GetFormDataByInstanceQueryHandler(db);
var query = new GetFormDataByInstanceQuery(InstanceId: instanceId);
// Act
var result = await handler.Handle(query, CancellationToken.None);
// Assert
result.Should().NotBeNull();
result!.Id.Should().Be(formDataId);
result.FormDefinitionId.Should().Be(formId);
result.InstanceId.Should().Be(instanceId);
result.DataJson.Should().Be(dataJson);
}
[Fact]
public async Task GetFormDataByInstance_NotFound_ReturnsNull()
{
// Arrange
await using var db = _fixture.CreateDbContext(testName: nameof(GetFormDataByInstance_NotFound_ReturnsNull));
var handler = new GetFormDataByInstanceQueryHandler(db);
var query = new GetFormDataByInstanceQuery(InstanceId: Guid.NewGuid());
// Act
var result = await handler.Handle(query, CancellationToken.None);
// Assert
result.Should().BeNull();
}
}