|
|
@@ -0,0 +1,128 @@
|
|
|
+package com.ruoyi.app.mendian;
|
|
|
+
|
|
|
+import com.ruoyi.common.annotation.Anonymous;
|
|
|
+import com.ruoyi.common.utils.MessageUtils;
|
|
|
+import com.ruoyi.system.domain.PosFood;
|
|
|
+import com.ruoyi.system.service.IPosFoodService;
|
|
|
+import com.ruoyi.system.service.MerchantStoreAccessService;
|
|
|
+import com.ruoyi.system.utils.Auth;
|
|
|
+import org.junit.jupiter.api.AfterEach;
|
|
|
+import org.junit.jupiter.api.BeforeEach;
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
+import org.mockito.ArgumentCaptor;
|
|
|
+import org.mockito.MockedStatic;
|
|
|
+import org.springframework.http.MediaType;
|
|
|
+import org.springframework.security.access.prepost.PreAuthorize;
|
|
|
+import org.springframework.test.util.ReflectionTestUtils;
|
|
|
+import org.springframework.test.web.servlet.MockMvc;
|
|
|
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
|
|
+import org.springframework.web.bind.annotation.PutMapping;
|
|
|
+
|
|
|
+import java.lang.reflect.Method;
|
|
|
+import java.util.Arrays;
|
|
|
+
|
|
|
+import static org.junit.jupiter.api.Assertions.*;
|
|
|
+import static org.mockito.ArgumentMatchers.any;
|
|
|
+import static org.mockito.ArgumentMatchers.anyString;
|
|
|
+import static org.mockito.Mockito.*;
|
|
|
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
|
|
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
|
|
+
|
|
|
+/** 平台审核独立于商家 token/门店归属,只更新审核状态,不能误走商品保存或规格处理。 */
|
|
|
+class PosFoodControllerAuditTest {
|
|
|
+ private IPosFoodService foods;
|
|
|
+ private MerchantStoreAccessService merchantAccess;
|
|
|
+ private MockMvc mvc;
|
|
|
+ private MockedStatic<MessageUtils> messages;
|
|
|
+
|
|
|
+ @BeforeEach
|
|
|
+ void setup() {
|
|
|
+ messages = mockStatic(MessageUtils.class);
|
|
|
+ messages.when(() -> MessageUtils.message(anyString())).thenAnswer(call -> call.getArgument(0));
|
|
|
+ PosFoodController controller = new PosFoodController();
|
|
|
+ foods = mock(IPosFoodService.class);
|
|
|
+ merchantAccess = mock(MerchantStoreAccessService.class);
|
|
|
+ ReflectionTestUtils.setField(controller, "posFoodService", foods);
|
|
|
+ ReflectionTestUtils.setField(controller, "merchantStoreAccessService", merchantAccess);
|
|
|
+ mvc = MockMvcBuilders.standaloneSetup(controller).build();
|
|
|
+ when(foods.updatePosFood(any(PosFood.class))).thenReturn(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ @AfterEach
|
|
|
+ void cleanup() { messages.close(); }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void platformAuditUsesExistingEditPermissionAndNeverMerchantAuth() {
|
|
|
+ Method audit = Arrays.stream(PosFoodController.class.getDeclaredMethods())
|
|
|
+ .filter(method -> method.isAnnotationPresent(PutMapping.class)
|
|
|
+ && Arrays.asList(method.getAnnotation(PutMapping.class).value()).contains("/audit"))
|
|
|
+ .findFirst().orElse(null);
|
|
|
+ assertNotNull(audit, "platform audit endpoint must exist");
|
|
|
+ assertNotNull(audit.getAnnotation(PreAuthorize.class));
|
|
|
+ assertEquals("@ss.hasPermi('chanting:food:edit')", audit.getAnnotation(PreAuthorize.class).value());
|
|
|
+ assertNull(audit.getAnnotation(Anonymous.class));
|
|
|
+ assertNull(audit.getAnnotation(Auth.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void approveAndRevokeWorkWithoutMerchantTokenHeader() throws Exception {
|
|
|
+ for (String state : new String[] {"1", "0"}) {
|
|
|
+ mvc.perform(put("/chanting/food/audit").header("Authorization", "Bearer platform-test")
|
|
|
+ .contentType(MediaType.APPLICATION_JSON).content("{\"id\":9137,\"toExamine\":\"" + state + "\"}"))
|
|
|
+ .andExpect(status().isOk()).andExpect(jsonPath("$.code").value(200));
|
|
|
+ }
|
|
|
+ ArgumentCaptor<PosFood> update = ArgumentCaptor.forClass(PosFood.class);
|
|
|
+ verify(foods, times(2)).updatePosFood(update.capture());
|
|
|
+ assertEquals("1", update.getAllValues().get(0).getToExamine());
|
|
|
+ assertEquals("0", update.getAllValues().get(1).getToExamine());
|
|
|
+ verifyNoInteractions(merchantAccess);
|
|
|
+ verify(foods, never()).saveOrUpdate(any(PosFood.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void auditIgnoresInjectedContentAndNeverChangesStoreOrSpecs() throws Exception {
|
|
|
+ mvc.perform(put("/chanting/food/audit").contentType(MediaType.APPLICATION_JSON)
|
|
|
+ .content("{\"id\":9137,\"toExamine\":\"1\",\"mdid\":999,\"price\":1,\"name\":\"injected\",\"stackingUp\":\"0\",\"foodSpecs\":[]}"))
|
|
|
+ .andExpect(status().isOk()).andExpect(jsonPath("$.code").value(200));
|
|
|
+ ArgumentCaptor<PosFood> update = ArgumentCaptor.forClass(PosFood.class);
|
|
|
+ verify(foods).updatePosFood(update.capture());
|
|
|
+ PosFood changed = update.getValue();
|
|
|
+ assertEquals(9137L, changed.getId());
|
|
|
+ assertEquals("1", changed.getToExamine());
|
|
|
+ assertNull(changed.getMdid());
|
|
|
+ assertNull(changed.getPrice());
|
|
|
+ assertNull(changed.getName());
|
|
|
+ assertNull(changed.getStackingUp());
|
|
|
+ assertNull(changed.getFoodSpecs());
|
|
|
+ assertNull(changed.getRecommend());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void invalidIdOrAuditStateIsRejectedBeforeWrite() throws Exception {
|
|
|
+ for (String payload : new String[] {"{}", "{\"id\":0,\"toExamine\":\"1\"}",
|
|
|
+ "{\"id\":-1,\"toExamine\":\"1\"}", "{\"id\":9137}",
|
|
|
+ "{\"id\":9137,\"toExamine\":\"2\"}", "{\"id\":9137,\"toExamine\":\"\"}"}) {
|
|
|
+ mvc.perform(put("/chanting/food/audit").contentType(MediaType.APPLICATION_JSON).content(payload))
|
|
|
+ .andExpect(status().isOk()).andExpect(jsonPath("$.code").value(500))
|
|
|
+ .andExpect(jsonPath("$.msg").value("food.audit.invalid"));
|
|
|
+ }
|
|
|
+ verifyNoInteractions(foods);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void missingFoodDoesNotCreateProductOrReportSuccess() throws Exception {
|
|
|
+ when(foods.updatePosFood(any(PosFood.class))).thenReturn(0);
|
|
|
+ mvc.perform(put("/chanting/food/audit").contentType(MediaType.APPLICATION_JSON)
|
|
|
+ .content("{\"id\":9137,\"toExamine\":\"1\"}"))
|
|
|
+ .andExpect(status().isOk()).andExpect(jsonPath("$.code").value(500));
|
|
|
+ verify(foods, never()).saveOrUpdate(any(PosFood.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void originalMerchantEndpointStillRequiresMerchantToken() throws Exception {
|
|
|
+ mvc.perform(put("/chanting/food/changerecommend").header("Authorization", "Bearer platform-test")
|
|
|
+ .contentType(MediaType.APPLICATION_JSON).content("{\"id\":9137,\"toExamine\":\"1\"}"))
|
|
|
+ .andExpect(status().isBadRequest());
|
|
|
+ verifyNoInteractions(foods, merchantAccess);
|
|
|
+ }
|
|
|
+}
|