using FluentAssertions; using Workflow.Application.BuildFlow.Common.Services; using Xunit; namespace BuildFlow.Workflow.Tests.BuildFlow; public class FormSchemaExcelServiceTests { private static readonly string SampleSchema = """ { "type": "object", "properties": { "name": { "type": "string", "title": "姓名", "required": true, "x-component": "Input" }, "amount": { "type": "number", "title": "金额", "x-component": "InputNumber" }, "approved": { "type": "boolean", "title": "是否通过", "x-component": "Switch" } } } """; private readonly FormSchemaExcelService _service = new(); [Fact] public void BuildTemplate_ReturnsXlsxWithHeadersAndSampleRow() { var bytes = _service.BuildTemplate(SampleSchema, "测试表单"); bytes.Should().NotBeNullOrEmpty(); using var ms = new MemoryStream(bytes); using var wb = new ClosedXML.Excel.XLWorkbook(ms); var ws = wb.Worksheets.First(); ws.Cell(1, 1).GetString().Should().Be("姓名"); ws.Cell(1, 2).GetString().Should().Be("金额"); ws.Cell(1, 3).GetString().Should().Be("是否通过"); // 第二行应为示例值 ws.Cell(2, 1).GetString().Should().NotBeNullOrEmpty(); } [Fact] public void Parse_RoundTripsHeaderAndRowValues() { var templateBytes = _service.BuildTemplate(SampleSchema, "测试表单"); // 用模版本身的示例行替换为真实数据后解析 using var ms = new MemoryStream(templateBytes); using var wb = new ClosedXML.Excel.XLWorkbook(ms); var ws = wb.Worksheets.First(); ws.Cell(2, 1).Value = "张三"; ws.Cell(2, 2).Value = 100; ws.Cell(2, 3).Value = "true"; using var outMs = new MemoryStream(); wb.SaveAs(outMs); var rows = _service.Parse(SampleSchema, outMs.ToArray()); rows.Should().HaveCount(1); var row = rows[0]; row.RowNumber.Should().Be(2); // JSON 默认对非 ASCII 转义为 \uXXXX,验证 key 存在 + 张三的转义序列存在 row.JsonObject.Should().Contain("name"); row.JsonObject.Should().Contain("\\u5F20\\u4E09"); // 必填字段已填,应无错误 row.Errors.Should().BeEmpty(); } [Fact] public void Parse_ReportsErrorWhenRequiredFieldEmpty() { var templateBytes = _service.BuildTemplate(SampleSchema, "测试表单"); using var ms = new MemoryStream(templateBytes); using var wb = new ClosedXML.Excel.XLWorkbook(ms); var ws = wb.Worksheets.First(); ws.Cell(2, 1).Value = string.Empty; // 姓名(必填)为空 ws.Cell(2, 2).Value = 50; using var outMs = new MemoryStream(); wb.SaveAs(outMs); var rows = _service.Parse(SampleSchema, outMs.ToArray()); rows.Should().HaveCount(1); rows[0].Errors.Should().ContainSingle(e => e.Message.Contains("必填")); } [Fact] public void BuildExport_OutputsDataRowsByFieldOrder() { var dataRows = new[] { """{"name":"李四","amount":200,"approved":true}""", """{"name":"王五","amount":300,"approved":false}""", }; var bytes = _service.BuildExport(SampleSchema, "测试表单", dataRows); using var ms = new MemoryStream(bytes); using var wb = new ClosedXML.Excel.XLWorkbook(ms); var ws = wb.Worksheets.First(); ws.Cell(2, 1).GetString().Should().Be("李四"); ws.Cell(3, 1).GetString().Should().Be("王五"); } }