| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mdm.service.agent; |
| | | |
| | | import cn.iocoder.yudao.module.mdm.controller.admin.agent.vo.MdmAgentImportCommitReqVO; |
| | | 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.agent.vo.MdmAgentImportReceiptRespVO; |
| | | 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.agent.MdmAgentImportCommandDO; |
| | | import cn.iocoder.yudao.module.mdm.dal.mysql.agent.MdmAgentImportCommandMapper; |
| | | import cn.iocoder.yudao.module.mdm.dal.mysql.agent.MdmAgentImportCommandRowMapper; |
| | | 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.AgentBridgeException; |
| | | import cn.iocoder.yudao.module.system.framework.agent.AgentPrincipal; |
| | | import org.junit.jupiter.api.BeforeEach; |
| | | 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 org.springframework.transaction.PlatformTransactionManager; |
| | | import org.springframework.transaction.TransactionDefinition; |
| | | import org.springframework.transaction.TransactionStatus; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.concurrent.atomic.AtomicReference; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.*; |
| | | import static org.mockito.ArgumentMatchers.any; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | | @ExtendWith(MockitoExtension.class) |
| | | @MockitoSettings(strictness = Strictness.LENIENT) |
| | | class MdmAgentImportCommandServiceTest { |
| | | |
| | | private static final String PREFLIGHT_HASH = "b".repeat(64); |
| | | private static final String SOURCE_SHA = "a".repeat(64); |
| | | |
| | | @Mock private MdmAgentImportPreflightService preflightService; |
| | | @Mock private MdmItemService itemService; |
| | | @Mock private MdmAgentImportPolicy policy; |
| | | @Mock private MdmAgentImportCommandMapper commandMapper; |
| | | @Mock private MdmAgentImportCommandRowMapper commandRowMapper; |
| | | @Mock private PlatformTransactionManager transactionManager; |
| | | @InjectMocks private MdmAgentImportCommandService service; |
| | | |
| | | private final AgentPrincipal principal = new AgentPrincipal(7L, "live"); |
| | | |
| | | @BeforeEach |
| | | void setUp() { |
| | | when(policy.isWriteEnabled()).thenReturn(true); |
| | | when(transactionManager.getTransaction(any(TransactionDefinition.class))) |
| | | .thenReturn(mock(TransactionStatus.class)); |
| | | when(itemService.buildImportContext(any())).thenReturn(new MdmItemImportContext( |
| | | Map.of("RAW", 11L), Map.of("RAW", "åæ"), |
| | | Map.of("PCS", 22L), Map.of("PCS", "个"))); |
| | | when(itemService.resolveImportRow(any(MdmItemImportExcelVO.class), any())) |
| | | .thenAnswer(invocation -> MdmItemRowResolution.success(new MdmItemSaveReqVO())); |
| | | when(itemService.getItemListByCode(any())).thenReturn(List.of()); |
| | | when(itemService.createItem(any())).thenReturn(77L); |
| | | when(commandRowMapper.selectListByCommandId(any())).thenReturn(List.of()); |
| | | } |
| | | |
| | | @Test |
| | | void writeDisabledRejects() { |
| | | when(policy.isWriteEnabled()).thenReturn(false); |
| | | AgentBridgeException ex = assertThrows(AgentBridgeException.class, |
| | | () -> service.commit(request("ç©æA"), principal)); |
| | | assertEquals("WRITE_DISABLED", ex.getErrorCode()); |
| | | verify(itemService, never()).createItem(any()); |
| | | } |
| | | |
| | | @Test |
| | | void repeatedCommitCreatesItemOnceAndReplaysReceipt() { |
| | | stubPreflight(PREFLIGHT_HASH); |
| | | AtomicReference<MdmAgentImportCommandDO> inserted = captureInsertedCommand(); |
| | | |
| | | MdmAgentImportReceiptRespVO first = service.commit(request("ç©æA"), principal); |
| | | when(commandMapper.selectByCommandId(7L, "cmd-1")).thenReturn(inserted.get()); |
| | | MdmAgentImportReceiptRespVO second = service.commit(request("ç©æA"), principal); |
| | | |
| | | assertEquals("SUCCEEDED", first.getStatus()); |
| | | assertEquals("SUCCEEDED", second.getStatus()); |
| | | assertEquals(first.getPreflightHash(), second.getPreflightHash()); |
| | | verify(itemService, times(1)).createItem(any()); |
| | | } |
| | | |
| | | @Test |
| | | void sameCommandWithDifferentContentConflicts() { |
| | | when(preflightService.preflight(any(), any())).thenReturn(preflight(PREFLIGHT_HASH)); |
| | | AtomicReference<MdmAgentImportCommandDO> inserted = captureInsertedCommand(); |
| | | service.commit(request("ç©æA"), principal); |
| | | when(commandMapper.selectByCommandId(7L, "cmd-1")).thenReturn(inserted.get()); |
| | | when(commandMapper.selectByIdempotencyKey(7L, "idem-1")).thenReturn(null); |
| | | |
| | | AgentBridgeException ex = assertThrows(AgentBridgeException.class, |
| | | () -> service.commit(request("ç©æB"), principal)); |
| | | assertEquals("COMMAND_ID_CONFLICT", ex.getErrorCode()); |
| | | } |
| | | |
| | | @Test |
| | | void stalePreflightIsRejected() { |
| | | stubPreflight("c".repeat(64)); |
| | | |
| | | AgentBridgeException ex = assertThrows(AgentBridgeException.class, |
| | | () -> service.commit(request("ç©æA"), principal)); |
| | | assertEquals("PLAN_STALE", ex.getErrorCode()); |
| | | verify(commandMapper, never()).insert(any(MdmAgentImportCommandDO.class)); |
| | | } |
| | | |
| | | @Test |
| | | void missingCommandReturnsNotFound() { |
| | | when(commandMapper.selectByCommandId(7L, "cmd-x")).thenReturn(null); |
| | | |
| | | AgentBridgeException ex = assertThrows(AgentBridgeException.class, |
| | | () -> service.getCommand("cmd-x", principal)); |
| | | assertEquals("COMMAND_NOT_FOUND", ex.getErrorCode()); |
| | | } |
| | | |
| | | private void stubPreflight(String preflightHash) { |
| | | when(preflightService.preflight(any(), any())).thenReturn(preflight(preflightHash)); |
| | | } |
| | | |
| | | private static MdmAgentImportPreflightRespVO preflight(String hash) { |
| | | return MdmAgentImportPreflightRespVO.builder() |
| | | .preflightHash(hash) |
| | | .canCommit(true) |
| | | .rows(List.of(MdmAgentImportPreflightRespVO.Row.builder() |
| | | .candidateId("c-0").action("CREATE").build())) |
| | | .summary(MdmAgentImportPreflightRespVO.Summary.builder() |
| | | .total(1).create(1).skip(0).blocked(0).build()) |
| | | .build(); |
| | | } |
| | | |
| | | private AtomicReference<MdmAgentImportCommandDO> captureInsertedCommand() { |
| | | AtomicReference<MdmAgentImportCommandDO> holder = new AtomicReference<>(); |
| | | doAnswer(invocation -> { |
| | | MdmAgentImportCommandDO command = invocation.getArgument(0); |
| | | command.setId(1L); |
| | | holder.set(command); |
| | | return 1; |
| | | }).when(commandMapper).insert(any(MdmAgentImportCommandDO.class)); |
| | | return holder; |
| | | } |
| | | |
| | | private static MdmAgentImportCommitReqVO request(String name) { |
| | | MdmAgentImportPreflightReqVO.Row row = new MdmAgentImportPreflightReqVO.Row(); |
| | | row.setCandidateId("c-0"); |
| | | row.setRowNo(1); |
| | | row.setCode("AGENT-E2E-1"); |
| | | row.setName(name); |
| | | row.setCategoryCode("RAW"); |
| | | row.setUnitMeasureCode("PCS"); |
| | | |
| | | MdmAgentImportCommitReqVO reqVO = new MdmAgentImportCommitReqVO(); |
| | | reqVO.setCommandId("cmd-1"); |
| | | reqVO.setIdempotencyKey("idem-1"); |
| | | reqVO.setAttachmentId(5L); |
| | | reqVO.setSessionId(21L); |
| | | reqVO.setSourceSha256(SOURCE_SHA); |
| | | reqVO.setPreflightHash(PREFLIGHT_HASH); |
| | | reqVO.setProposalRevision("rev-1"); |
| | | reqVO.setRows(List.of(row)); |
| | | return reqVO; |
| | | } |
| | | } |