|
|
@@ -0,0 +1,130 @@
|
|
|
+package com.ruoyi.app.user;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
|
|
+import com.ruoyi.common.core.domain.AjaxResult;
|
|
|
+import com.ruoyi.common.utils.spring.SpringUtils;
|
|
|
+import com.ruoyi.system.domain.InfoUser;
|
|
|
+import com.ruoyi.system.domain.RiderPosition;
|
|
|
+import com.ruoyi.system.service.IInfoUserService;
|
|
|
+import com.ruoyi.system.service.IRiderPositionService;
|
|
|
+import com.ruoyi.system.utils.JwtUtil;
|
|
|
+import org.junit.jupiter.api.AfterAll;
|
|
|
+import org.junit.jupiter.api.BeforeAll;
|
|
|
+import org.junit.jupiter.api.BeforeEach;
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
+import org.mockito.ArgumentCaptor;
|
|
|
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
|
|
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
|
|
+import org.springframework.context.support.StaticMessageSource;
|
|
|
+import org.springframework.dao.DuplicateKeyException;
|
|
|
+import org.springframework.test.util.ReflectionTestUtils;
|
|
|
+
|
|
|
+import java.util.Locale;
|
|
|
+
|
|
|
+import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
+import static org.mockito.ArgumentMatchers.any;
|
|
|
+import static org.mockito.ArgumentMatchers.anyBoolean;
|
|
|
+import static org.mockito.ArgumentMatchers.eq;
|
|
|
+import static org.mockito.Mockito.mock;
|
|
|
+import static org.mockito.Mockito.never;
|
|
|
+import static org.mockito.Mockito.times;
|
|
|
+import static org.mockito.Mockito.verify;
|
|
|
+import static org.mockito.Mockito.when;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 骑手定位上传:一人一条约束下的容错——历史重复行不再抛 selectOne 多行异常;
|
|
|
+ * 并发插入撞唯一索引转更新;新增/更新两分支行为不变。
|
|
|
+ */
|
|
|
+class RiderPositionControllerTest {
|
|
|
+
|
|
|
+ private static ConfigurableListableBeanFactory originalBeanFactory;
|
|
|
+
|
|
|
+ private final IRiderPositionService riderPositionService = mock(IRiderPositionService.class);
|
|
|
+ private final IInfoUserService infoUserService = mock(IInfoUserService.class);
|
|
|
+ private final RiderPositionController controller = new RiderPositionController();
|
|
|
+
|
|
|
+ @BeforeAll
|
|
|
+ static void initializeBeanFactory() {
|
|
|
+ originalBeanFactory = (ConfigurableListableBeanFactory)
|
|
|
+ ReflectionTestUtils.getField(SpringUtils.class, "beanFactory");
|
|
|
+ DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
|
|
+ StaticMessageSource messageSource = new StaticMessageSource();
|
|
|
+ messageSource.addMessage("no.action.success", Locale.getDefault(), "操作成功");
|
|
|
+ beanFactory.registerSingleton("messageSource", messageSource);
|
|
|
+ new SpringUtils().postProcessBeanFactory(beanFactory);
|
|
|
+ }
|
|
|
+
|
|
|
+ @AfterAll
|
|
|
+ static void restoreBeanFactory() {
|
|
|
+ new SpringUtils().postProcessBeanFactory(originalBeanFactory);
|
|
|
+ }
|
|
|
+
|
|
|
+ @BeforeEach
|
|
|
+ void setUp() {
|
|
|
+ ReflectionTestUtils.setField(controller, "riderPositionService", riderPositionService);
|
|
|
+ ReflectionTestUtils.setField(controller, "infoUserService", infoUserService);
|
|
|
+ when(infoUserService.getById(any())).thenReturn(new InfoUser());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void usesTolerantGetOneSoDuplicateRowsNoLongerCrash() {
|
|
|
+ RiderPosition existing = new RiderPosition();
|
|
|
+ existing.setId(7L);
|
|
|
+ existing.setRiderId(9L);
|
|
|
+ // 多行历史数据下 getOne(qw,false) 返回第一条,不再抛"Expected one result"
|
|
|
+ when(riderPositionService.getOne(any(Wrapper.class), eq(false))).thenReturn(existing);
|
|
|
+ when(riderPositionService.saveOrUpdate(any(RiderPosition.class))).thenReturn(true);
|
|
|
+
|
|
|
+ AjaxResult result = controller.setRiderposition(JwtUtil.token("9", "user"), new RiderPosition());
|
|
|
+
|
|
|
+ assertEquals(200, ((Number) result.get(AjaxResult.CODE_TAG)).intValue());
|
|
|
+ // 单参 getOne 默认 throwEx=true,是本次线上 500 的直接来源,禁止再回到该调用
|
|
|
+ verify(riderPositionService, never()).getOne(any(Wrapper.class));
|
|
|
+ ArgumentCaptor<RiderPosition> captor = ArgumentCaptor.forClass(RiderPosition.class);
|
|
|
+ verify(riderPositionService).saveOrUpdate(captor.capture());
|
|
|
+ assertEquals(7L, captor.getValue().getId());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void concurrentInsertOnUniqueKeyFallsBackToUpdateByRider() {
|
|
|
+ when(riderPositionService.getOne(any(Wrapper.class), anyBoolean())).thenReturn(null);
|
|
|
+ // 并发上传双双查空:第二个 insert 撞 rider_id 唯一索引
|
|
|
+ when(riderPositionService.saveOrUpdate(any(RiderPosition.class)))
|
|
|
+ .thenThrow(new DuplicateKeyException("uk_rider_id"));
|
|
|
+ when(riderPositionService.update(any(RiderPosition.class), any(Wrapper.class))).thenReturn(true);
|
|
|
+
|
|
|
+ AjaxResult result = controller.setRiderposition(JwtUtil.token("9", "user"), new RiderPosition());
|
|
|
+
|
|
|
+ assertEquals(200, ((Number) result.get(AjaxResult.CODE_TAG)).intValue());
|
|
|
+ // 兜底转按 rider_id 更新已有记录,请求不 500
|
|
|
+ verify(riderPositionService).update(any(RiderPosition.class), any(Wrapper.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void firstUploadInsertsNewRecordWithRiderId() {
|
|
|
+ when(riderPositionService.getOne(any(Wrapper.class), anyBoolean())).thenReturn(null);
|
|
|
+ when(riderPositionService.saveOrUpdate(any(RiderPosition.class))).thenReturn(true);
|
|
|
+
|
|
|
+ AjaxResult result = controller.setRiderposition(JwtUtil.token("9", "user"), new RiderPosition());
|
|
|
+
|
|
|
+ assertEquals(200, ((Number) result.get(AjaxResult.CODE_TAG)).intValue());
|
|
|
+ ArgumentCaptor<RiderPosition> captor = ArgumentCaptor.forClass(RiderPosition.class);
|
|
|
+ verify(riderPositionService).saveOrUpdate(captor.capture());
|
|
|
+ assertEquals(9L, captor.getValue().getRiderId());
|
|
|
+ verify(riderPositionService, never()).update(any(RiderPosition.class), any(Wrapper.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void secondUploadUpdatesExistingRecordById() {
|
|
|
+ RiderPosition existing = new RiderPosition();
|
|
|
+ existing.setId(7L);
|
|
|
+ when(riderPositionService.getOne(any(Wrapper.class), anyBoolean())).thenReturn(existing);
|
|
|
+ when(riderPositionService.saveOrUpdate(any(RiderPosition.class))).thenReturn(true);
|
|
|
+
|
|
|
+ AjaxResult result = controller.setRiderposition(JwtUtil.token("9", "user"), new RiderPosition());
|
|
|
+
|
|
|
+ assertEquals(200, ((Number) result.get(AjaxResult.CODE_TAG)).intValue());
|
|
|
+ verify(riderPositionService, times(1)).saveOrUpdate(any(RiderPosition.class));
|
|
|
+ verify(riderPositionService, never()).update(any(RiderPosition.class), any(Wrapper.class));
|
|
|
+ }
|
|
|
+}
|