liu
2 天以前 43aab58c9ecb1203770ba382efbdbae299ca87ec
yudao-module-mdm/src/test/java/cn/iocoder/yudao/module/mdm/service/agent/MdmAgentImportCommandServiceTest.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,171 @@
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;
    }
}