package cn.iocoder.yudao.module.mes.service.trace.integrity; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.module.mes.controller.admin.trace.integrity.vo.MesTraceIntegrityVerifyRespVO; import cn.iocoder.yudao.module.mes.dal.dataobject.trace.consumer.MesTraceConsumerScanEventDO; import cn.iocoder.yudao.module.mes.dal.dataobject.trace.integrity.MesTraceIntegrityRecordDO; import cn.iocoder.yudao.module.mes.dal.mysql.trace.consumer.MesTraceConsumerScanEventMapper; import cn.iocoder.yudao.module.mes.dal.mysql.trace.integrity.MesTraceIntegrityRecordMapper; import cn.iocoder.yudao.module.mes.enums.trace.MesTraceIntegrityBizTypeEnum; import jakarta.annotation.Resource; import org.junit.jupiter.api.Test; import org.springframework.context.annotation.Import; import java.time.LocalDateTime; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.MES_TRACE_INTEGRITY_BIZ_TYPE_INVALID; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.MES_TRACE_INTEGRITY_RECORD_NOT_EXISTS; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link MesTraceIntegrityServiceImpl} 的单元测试 * * @author 超级管理员 */ @Import(MesTraceIntegrityServiceImpl.class) public class MesTraceIntegrityServiceImplTest extends BaseDbUnitTest { @Resource private MesTraceIntegrityService integrityService; @Resource private MesTraceIntegrityRecordMapper integrityRecordMapper; @Resource private MesTraceConsumerScanEventMapper scanEventMapper; @Test public void testRecordScanIntegrity_生成摘要并入库() { MesTraceConsumerScanEventDO event = insertEvent("江苏省", "南京市"); integrityService.recordScanIntegrity(event); MesTraceIntegrityRecordDO record = integrityRecordMapper.selectByBiz( MesTraceIntegrityBizTypeEnum.SCAN.getType(), event.getId()); assertNotNull(record); assertEquals(64, record.getContentHash().length()); assertEquals(event.getNormalizedCode(), record.getBizCode()); } @Test public void testVerify_数据完整通过() { MesTraceConsumerScanEventDO event = insertEvent("江苏省", "南京市"); integrityService.recordScanIntegrity(event); MesTraceIntegrityVerifyRespVO vo = integrityService.verify( MesTraceIntegrityBizTypeEnum.SCAN.getType(), event.getId()); assertTrue(vo.getMatched()); assertEquals(vo.getStoredHash(), vo.getCurrentHash()); assertEquals(event.getNormalizedCode(), vo.getBizCode()); } @Test public void testVerify_篡改检测失败() { MesTraceConsumerScanEventDO event = insertEvent("江苏省", "南京市"); integrityService.recordScanIntegrity(event); // 模拟事后篡改扫码地区 scanEventMapper.updateById(new MesTraceConsumerScanEventDO().setId(event.getId()).setProvince("浙江省")); MesTraceIntegrityVerifyRespVO vo = integrityService.verify( MesTraceIntegrityBizTypeEnum.SCAN.getType(), event.getId()); assertFalse(vo.getMatched()); assertEquals("数据不一致,可能存在篡改", vo.getMessage()); } @Test public void testVerify_覆盖更新不产生重复记录() { MesTraceConsumerScanEventDO event = insertEvent("江苏省", "南京市"); integrityService.recordScanIntegrity(event); integrityService.recordScanIntegrity(event); assertEquals(1L, integrityRecordMapper.selectCount()); MesTraceIntegrityRecordDO record = integrityRecordMapper.selectByBiz( MesTraceIntegrityBizTypeEnum.SCAN.getType(), event.getId()); assertEquals(64, record.getContentHash().length()); } @Test public void testVerify_无摘要记录异常() { ServiceException ex = assertThrows(ServiceException.class, () -> integrityService.verify(MesTraceIntegrityBizTypeEnum.SCAN.getType(), 99999L)); assertEquals(MES_TRACE_INTEGRITY_RECORD_NOT_EXISTS.getCode(), ex.getCode()); } @Test public void testVerify_非法业务类型() { ServiceException ex = assertThrows(ServiceException.class, () -> integrityService.verify("XXX", 1L)); assertEquals(MES_TRACE_INTEGRITY_BIZ_TYPE_INVALID.getCode(), ex.getCode()); } @Test public void testRecordScanIntegrity_null事件容错() { integrityService.recordScanIntegrity(null); integrityService.recordScanIntegrity(MesTraceConsumerScanEventDO.builder().id(null).build()); assertEquals(0L, integrityRecordMapper.selectCount()); } // ==================== 辅助方法 ==================== private MesTraceConsumerScanEventDO insertEvent(String province, String city) { MesTraceConsumerScanEventDO event = MesTraceConsumerScanEventDO.builder() .traceCode("BAG-00000001") .normalizedCode("BAG-00000001") .sourceType("BAG") .province(province) .city(city) .batchId(10L) .success(true) .eventTime(LocalDateTime.of(2026, 9, 1, 10, 0)) .build(); scanEventMapper.insert(event); return event; } }