diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/appearancepatent/service/support/AppearancePatentResultFormatTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/appearancepatent/service/support/AppearancePatentResultFormatTest.java new file mode 100644 index 00000000..ecca3e17 --- /dev/null +++ b/backend-java/src/test/java/com/nanri/aiimage/modules/appearancepatent/service/support/AppearancePatentResultFormatTest.java @@ -0,0 +1,110 @@ +package com.nanri.aiimage.modules.appearancepatent.service.support; + +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * task-218(离线可收部分):结果文件格式契约(真实 Sheet builder 纯调用)。 + * + * 调 AppearancePatentSheetBuilder 在内存 Workbook 生成 Sheet,断言 Sheet 名、主表列序、 + * 原因表列名冻结、空行也能产出(空结果文件);文件名 `-result.xlsx` 与 ZIP 结构 + * 由运行时组装/上传环节生成,依赖真实流水线(env-gated 部分另行验证)。 + */ +class AppearancePatentResultFormatTest { + + private static Workbook build() { + Workbook wb = new XSSFWorkbook(); + AppearancePatentSheetBuilder.buildResultSheet(wb, List.of(), Map.of()); + return wb; + } + + private static List expectedMainHeaders() { + List headers = new ArrayList<>(AppearancePatentSheetBuilder.RESULT_HEADERS); + headers.add(6, "标题"); + headers.add(7, "图片链接"); + headers.add(8, "sku"); + return headers; + } + + @Test + void mainSheetNameFrozen() throws Exception { + try (Workbook wb = build()) { + assertNotNull(wb.getSheet("外观专利检测结果"), "主 Sheet 名应冻结为 外观专利检测结果"); + } + } + + @Test + void reasonSheetNameFrozen() throws Exception { + try (Workbook wb = build()) { + assertNotNull(wb.getSheet("原因"), "原因 Sheet 名应冻结为 原因"); + } + } + + @Test + void mainHeaderColumnOrderMatchesContract() throws Exception { + try (Workbook wb = build()) { + Sheet sheet = wb.getSheet("外观专利检测结果"); + Row header = sheet.getRow(0); + List actual = new ArrayList<>(); + for (int i = 0; i < header.getLastCellNum(); i++) { + actual.add(header.getCell(i).getStringCellValue()); + } + assertEquals(expectedMainHeaders(), actual, "主表列序应与 RESULT_HEADERS + 标题/图片链接/sku 一致"); + } + } + + @Test + void reasonHeaderColumnsFrozen() throws Exception { + try (Workbook wb = build()) { + Sheet reason = wb.getSheet("原因"); + Row header = reason.getRow(0); + assertEquals("ASIN", header.getCell(0).getStringCellValue()); + assertEquals("外观原因", header.getCell(1).getStringCellValue()); + assertEquals("专利原因", header.getCell(2).getStringCellValue()); + assertEquals("标题原因", header.getCell(3).getStringCellValue()); + } + } + + @Test + void emptyRowsStillGenerateSheets() throws Exception { + try (Workbook wb = build()) { + assertNotNull(wb.getSheet("外观专利检测结果")); + assertNotNull(wb.getSheet("原因")); + assertEquals(0, wb.getSheet("外观专利检测结果").getPhysicalNumberOfRows() - 1, + "空行时应仅表头,无数据行抛错"); + } + } + + @Test + void resultHeadersConstantDefinesBrandColumns() throws Exception { + List headers = AppearancePatentSheetBuilder.RESULT_HEADERS; + assertTrue(headers.contains("品牌") && headers.contains("价格") && headers.contains("结论") + && headers.contains("状态"), "RESULT_HEADERS 应含 品牌/价格/结论/状态"); + } + + @Test + void firstColumnIsRowId() throws Exception { + List headers = expectedMainHeaders(); + assertEquals("id", headers.get(0), "首列应为 id"); + assertEquals("asin", headers.get(1)); + } + + @Test + void columnCountMatchesContract() throws Exception { + try (Workbook wb = build()) { + int headerCount = wb.getSheet("外观专利检测结果").getRow(0).getLastCellNum(); + assertEquals(expectedMainHeaders().size(), headerCount, "列数应与契约表头一致"); + } + } +}