package cn.iocoder.yudao.module.mdm.service.agent; import cn.iocoder.yudao.module.mdm.controller.admin.agent.vo.MdmAgentImportPreflightReqVO; import cn.iocoder.yudao.module.mdm.controller.admin.agent.vo.MdmAgentImportPreflightRespVO; import cn.iocoder.yudao.module.mdm.controller.admin.item.vo.MdmItemImportExcelVO; import cn.iocoder.yudao.module.mdm.controller.admin.item.vo.MdmItemSaveReqVO; import cn.iocoder.yudao.module.mdm.dal.dataobject.item.MdmItemDO; import cn.iocoder.yudao.module.mdm.service.item.MdmItemImportContext; import cn.iocoder.yudao.module.mdm.service.item.MdmItemRowResolution; import cn.iocoder.yudao.module.mdm.service.item.MdmItemService; import cn.iocoder.yudao.module.system.framework.agent.AgentPrincipal; import cn.iocoder.yudao.module.system.service.storage.SystemAgentAttachmentRef; import cn.iocoder.yudao.module.system.service.storage.SystemAgentImportBridgeService; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.nio.file.Path; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class MdmAgentImportPreflightServiceTest { private static final String SHA = "a".repeat(64); @Mock private SystemAgentImportBridgeService bridgeService; @Mock private MdmItemService itemService; @Mock private MdmAgentImportPolicy policy; @InjectMocks private MdmAgentImportPreflightService service; private final AgentPrincipal principal = new AgentPrincipal(7L, 0L, "live"); @Test void classifiesCreateSkipAndBlockedRows() { stubAttachmentAndPolicy(); MdmItemDO existing = new MdmItemDO(); existing.setId(99L); when(itemService.getItemListByCode("A")).thenReturn(List.of()); when(itemService.getItemListByCode("B")).thenReturn(List.of(existing)); MdmAgentImportPreflightRespVO result = service.preflight( request("A", "B", "C"), principal); assertEquals(3, result.getSummary().getTotal()); assertEquals(1, result.getSummary().getCreate()); assertEquals(1, result.getSummary().getSkip()); assertEquals(1, result.getSummary().getBlocked()); assertEquals("CREATE", result.getRows().get(0).getAction()); assertEquals("SKIP", result.getRows().get(1).getAction()); assertEquals(99L, result.getRows().get(1).getTargetItemId()); assertFalse(result.getRows().get(1).getWarnings().isEmpty()); assertEquals("BLOCKED", result.getRows().get(2).getAction()); assertEquals("CATEGORY_NOT_FOUND", result.getRows().get(2).getBlockers().get(0).getCode()); assertFalse(result.getCanCommit()); } @Test void duplicateCodeInFileIsBlocked() { stubAttachmentAndPolicy(); when(itemService.getItemListByCode(anyString())).thenReturn(List.of()); MdmAgentImportPreflightRespVO result = service.preflight( request("A", "A"), principal); assertEquals(2, result.getSummary().getBlocked()); assertEquals("DUPLICATE_CODE_IN_FILE", result.getRows().get(1).getBlockers().get(0).getCode()); } @Test void hashIsStableAcrossIdenticalRuns() { stubAttachmentAndPolicy(); when(itemService.getItemListByCode("A")).thenReturn(List.of()); MdmAgentImportPreflightRespVO first = service.preflight(request("A"), principal); MdmAgentImportPreflightRespVO second = service.preflight(request("A"), principal); assertTrue(first.getCanCommit()); assertEquals(first.getPreflightHash(), second.getPreflightHash()); } private void stubAttachmentAndPolicy() { when(bridgeService.requireOwnedAttachmentRef(5L)) .thenReturn(new SystemAgentAttachmentRef(5L, 21L, "materials.csv", 100L, Path.of("x"))); when(bridgeService.sha256OfAttachment(5L)).thenReturn(SHA); when(policy.getMaxRows()).thenReturn(5000); when(policy.isAllowMesSync()).thenReturn(false); MdmItemImportContext context = new MdmItemImportContext( Map.of("RAW", 11L), Map.of("RAW", "原料"), Map.of("PCS", 22L), Map.of("PCS", "个")); when(itemService.buildImportContext(any())).thenReturn(context); when(itemService.resolveImportRow(any(MdmItemImportExcelVO.class), any())) .thenAnswer(invocation -> MdmItemRowResolution.success(new MdmItemSaveReqVO())); } private static MdmAgentImportPreflightReqVO request(String... codes) { MdmAgentImportPreflightReqVO reqVO = new MdmAgentImportPreflightReqVO(); reqVO.setAttachmentId(5L); reqVO.setSessionId(21L); reqVO.setSourceSha256(SHA); reqVO.setProposalRevision("rev-1"); reqVO.setRows(java.util.stream.IntStream.range(0, codes.length).mapToObj(index -> { String code = codes[index]; MdmAgentImportPreflightReqVO.Row row = new MdmAgentImportPreflightReqVO.Row(); row.setCandidateId("c-" + index); row.setRowNo(index + 1); row.setCode(code); row.setName("物料" + code); row.setCategoryCode("C".equals(code) ? "NOPE" : "RAW"); row.setUnitMeasureCode("PCS"); return row; }).toList()); return reqVO; } }