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, 0L, "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(0L, 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(0L, 7L, "cmd-1")).thenReturn(inserted.get());
|
when(commandMapper.selectByIdempotencyKey(0L, 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(0L, 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;
|
}
|
}
|