|
@@ -0,0 +1,276 @@
|
|
|
|
|
+package com.ruoyi.app.mendian;
|
|
|
|
|
+
|
|
|
|
|
+import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
|
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
|
|
+import com.ruoyi.app.mendian.dto.ExportInput;
|
|
|
|
|
+import com.ruoyi.app.tableqrcode.TableQrcodeController;
|
|
|
|
|
+import com.ruoyi.common.exception.ServiceException;
|
|
|
|
|
+import com.ruoyi.common.utils.MessageUtils;
|
|
|
|
|
+import com.ruoyi.common.utils.poi.ExcelUtil;
|
|
|
|
|
+import com.ruoyi.system.domain.*;
|
|
|
|
|
+import com.ruoyi.system.mapper.*;
|
|
|
|
|
+import com.ruoyi.system.service.*;
|
|
|
|
|
+import com.ruoyi.system.utils.JwtUtil;
|
|
|
|
|
+import org.apache.ibatis.builder.MapperBuilderAssistant;
|
|
|
|
|
+import org.junit.jupiter.api.AfterEach;
|
|
|
|
|
+import org.junit.jupiter.api.BeforeEach;
|
|
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
|
|
+import org.mockito.MockedStatic;
|
|
|
|
|
+import org.springframework.mock.web.MockHttpServletResponse;
|
|
|
|
|
+import org.springframework.test.util.ReflectionTestUtils;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.*;
|
|
|
|
|
+import static org.mockito.ArgumentMatchers.*;
|
|
|
|
|
+import static org.mockito.Mockito.*;
|
|
|
|
|
+
|
|
|
|
|
+class MerchantLegacyEntryAccessTest {
|
|
|
|
|
+ private final ShindexController index = new ShindexController();
|
|
|
|
|
+ private final TableQrcodeController tables = new TableQrcodeController();
|
|
|
|
|
+ private final IInfoUserService users = mock(IInfoUserService.class);
|
|
|
|
|
+ private final IPosOrderService orders = mock(IPosOrderService.class);
|
|
|
|
|
+ private final IPosStoreService stores = mock(IPosStoreService.class);
|
|
|
|
|
+ private final ITableQrcodeService codes = mock(ITableQrcodeService.class);
|
|
|
|
|
+ private final InfoUserMapper userMapper = mock(InfoUserMapper.class);
|
|
|
|
|
+ private final MerchantSubaccountStoreMapper grants = mock(MerchantSubaccountStoreMapper.class);
|
|
|
|
|
+ private final PosStoreMapper storeMapper = mock(PosStoreMapper.class);
|
|
|
|
|
+ private final MerchantStoreAccessService access = new MerchantStoreAccessService(
|
|
|
|
|
+ userMapper, grants, storeMapper, mock(PosOrderMapper.class), mock(PosFoodMapper.class));
|
|
|
|
|
+ private final InfoUser subaccount = user(936L, "5");
|
|
|
|
|
+ private final InfoUser owner = user(801L, "1");
|
|
|
|
|
+ private final PosStore store = new PosStore();
|
|
|
|
|
+ private final TableQrcode table = code(70L, 168L);
|
|
|
|
|
+ private MockedStatic<MessageUtils> messages;
|
|
|
|
|
+ private String token;
|
|
|
|
|
+
|
|
|
|
|
+ @BeforeEach
|
|
|
|
|
+ void setUp() {
|
|
|
|
|
+ messages = mockStatic(MessageUtils.class);
|
|
|
|
|
+ messages.when(() -> MessageUtils.message(anyString())).thenAnswer(call -> call.getArgument(0));
|
|
|
|
|
+ TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), TableQrcode.class);
|
|
|
|
|
+ token = JwtUtil.setToken("936", "subaccount");
|
|
|
|
|
+ ReflectionTestUtils.setField(index, "infoUserService", users);
|
|
|
|
|
+ ReflectionTestUtils.setField(index, "posOrderService", orders);
|
|
|
|
|
+ ReflectionTestUtils.setField(tables, "infoUserService", users);
|
|
|
|
|
+ ReflectionTestUtils.setField(tables, "posStoreService", stores);
|
|
|
|
|
+ ReflectionTestUtils.setField(tables, "posOrderService", orders);
|
|
|
|
|
+ ReflectionTestUtils.setField(tables, "tableQrcodeService", codes);
|
|
|
|
|
+ ReflectionTestUtils.setField(index, "merchantStoreAccessService", access);
|
|
|
|
|
+ ReflectionTestUtils.setField(tables, "merchantStoreAccessService", access);
|
|
|
|
|
+ subaccount.setMerchantOwnerId(801L);
|
|
|
|
|
+ subaccount.setSubaccountStatus("0");
|
|
|
|
|
+ subaccount.setStoreId(null);
|
|
|
|
|
+ store.setId(168);
|
|
|
|
|
+ store.setUserId(801L);
|
|
|
|
|
+ store.setDelFlag("0");
|
|
|
|
|
+ when(users.selectInfoUserByUserId(936L)).thenReturn(subaccount);
|
|
|
|
|
+ when(users.selectInfoUserByUserId(801L)).thenReturn(owner);
|
|
|
|
|
+ when(userMapper.selectInfoUserByUserId(936L)).thenReturn(subaccount);
|
|
|
|
|
+ when(userMapper.selectInfoUserByUserId(801L)).thenReturn(owner);
|
|
|
|
|
+ when(grants.selectStoreIdsBySubaccountUserId(936L)).thenReturn(List.of(168L));
|
|
|
|
|
+ when(storeMapper.selectPosStoreById(168L)).thenReturn(store);
|
|
|
|
|
+ when(storeMapper.selectOwnedStoreIdsByUserId(801L)).thenReturn(List.of(168L));
|
|
|
|
|
+ when(stores.getOne(any(Wrapper.class))).thenReturn(store);
|
|
|
|
|
+ when(codes.selectTableQrcodeById(70L)).thenReturn(table);
|
|
|
|
|
+ when(codes.list(any(Wrapper.class))).thenReturn(List.of());
|
|
|
|
|
+ when(orders.page(any(), any(Wrapper.class))).thenReturn(new Page<PosOrder>(1, 10));
|
|
|
|
|
+ when(orders.list(any(Wrapper.class))).thenReturn(List.of());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @AfterEach
|
|
|
|
|
+ void tearDown() { messages.close(); }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void legacyListUsesOwnerAndAuthorizedStoreInBothStateBranches() {
|
|
|
|
|
+ index.getstoreorderlist(token, 1, 10, "168", "z01", "", "", "");
|
|
|
|
|
+ verify(orders).page(any(), argThat(query -> {
|
|
|
|
|
+ AbstractWrapper<?, ?, ?> wrapper = (AbstractWrapper<?, ?, ?>) query;
|
|
|
|
|
+ String sql = wrapper.getSqlSegment();
|
|
|
|
|
+ return sql.contains("sh_id") && sql.contains("md_id")
|
|
|
|
|
+ && wrapper.getParamNameValuePairs().containsValue("801")
|
|
|
|
|
+ && !wrapper.getParamNameValuePairs().containsValue("936");
|
|
|
|
|
+ }));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void legacyExportUsesOwnerAndAuthorizedStore() {
|
|
|
|
|
+ ExportInput input = new ExportInput();
|
|
|
|
|
+ input.setMdId("168");
|
|
|
|
|
+ input.setState("z01");
|
|
|
|
|
+ try (var excel = mockConstruction(ExcelUtil.class)) {
|
|
|
|
|
+ index.export(new MockHttpServletResponse(), token, input);
|
|
|
|
|
+ }
|
|
|
|
|
+ verify(orders).list(argThat((Wrapper<PosOrder> query) -> {
|
|
|
|
|
+ AbstractWrapper<?, ?, ?> wrapper = (AbstractWrapper<?, ?, ?>) query;
|
|
|
|
|
+ wrapper.getSqlSegment();
|
|
|
|
|
+ return wrapper.getParamNameValuePairs().containsValue("801")
|
|
|
|
|
+ && !wrapper.getParamNameValuePairs().containsValue("936");
|
|
|
|
|
+ }));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void stallListUsesBoundStoreWithoutMistakingStallUserForOrderOwner() {
|
|
|
|
|
+ subaccount.setUserType("4");
|
|
|
|
|
+ subaccount.setStoreId(168L);
|
|
|
|
|
+ index.getstoreorderlist(token, 1, 10, "168", "z01", "", "", "");
|
|
|
|
|
+ verify(orders).page(any(), argThat(query -> query.getSqlSegment().contains("md_id")
|
|
|
|
|
+ && !query.getSqlSegment().contains("sh_id")));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void stallExportCannotTargetAnotherStore() {
|
|
|
|
|
+ subaccount.setUserType("4");
|
|
|
|
|
+ subaccount.setStoreId(168L);
|
|
|
|
|
+ ExportInput input = new ExportInput();
|
|
|
|
|
+ input.setMdId("169");
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> index.export(new MockHttpServletResponse(), token, input));
|
|
|
|
|
+ verifyNoInteractions(orders);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void ordinaryOwnerRetainsAuthorizedTableManagement() {
|
|
|
|
|
+ String ownerToken = JwtUtil.setToken("801", "owner");
|
|
|
|
|
+ assertEquals(200, tables.list(ownerToken, 168L, null, null, null).get("code"));
|
|
|
|
|
+ assertEquals(200, tables.add(ownerToken, code(null, 168L)).get("code"));
|
|
|
|
|
+ assertEquals(200, tables.getInfo(ownerToken, 70L).get("code"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void authorizedSubaccountCanGetImageButCannotManagePublicCode() {
|
|
|
|
|
+ when(codes.generateQrcodeImage(70L)).thenReturn("image");
|
|
|
|
|
+ assertEquals(200, tables.getQrcodeImage(token, 70L).get("code"));
|
|
|
|
|
+ verify(codes).generateQrcodeImage(70L);
|
|
|
|
|
+ table.setStoreId(null);
|
|
|
|
|
+ table.setType(1);
|
|
|
|
|
+ assertEquals(500, tables.getInfo(token, 70L).get("code"));
|
|
|
|
|
+ assertEquals(500, tables.delete(token, 70L).get("code"));
|
|
|
|
|
+ verify(codes, never()).deleteTableQrcodeById(anyLong());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void legacyListRejectsAnotherStoreBeforeQuery() {
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> index.getstoreorderlist(token, 1, 10, "169", "", "", "", ""));
|
|
|
|
|
+ verifyNoInteractions(orders);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void revokedGrantBlocksExportEvenWithStaleSingleStoreId() {
|
|
|
|
|
+ grantsRevoked();
|
|
|
|
|
+ ExportInput input = new ExportInput();
|
|
|
|
|
+ input.setMdId("168");
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> index.export(new MockHttpServletResponse(), token, input));
|
|
|
|
|
+ verifyNoInteractions(orders);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void tableListAlwaysHasAuthorizedStorePredicate() {
|
|
|
|
|
+ tables.list(token, null, null, null, null);
|
|
|
|
|
+ verify(codes).list(argThat((Wrapper<TableQrcode> query) -> {
|
|
|
|
|
+ AbstractWrapper<?, ?, ?> wrapper = (AbstractWrapper<?, ?, ?>) query;
|
|
|
|
|
+ return wrapper.getSqlSegment().contains("store_id IN")
|
|
|
|
|
+ && wrapper.getParamNameValuePairs().containsValue(168L);
|
|
|
|
|
+ }));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void tableListWithoutGrantsCannotFallThroughToAllStores() {
|
|
|
|
|
+ grantsRevoked();
|
|
|
|
|
+ tables.list(token, null, null, null, null);
|
|
|
|
|
+ verify(codes).list(argThat((Wrapper<TableQrcode> query) -> {
|
|
|
|
|
+ AbstractWrapper<?, ?, ?> wrapper = (AbstractWrapper<?, ?, ?>) query;
|
|
|
|
|
+ return wrapper.getSqlSegment().contains("id =")
|
|
|
|
|
+ && wrapper.getParamNameValuePairs().containsValue(0L);
|
|
|
|
|
+ }));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void explicitUnauthorizedTableStoreIsRejected() {
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> tables.list(token, 169L, null, null, null));
|
|
|
|
|
+ verifyNoInteractions(codes);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void authorizedSubaccountCanCreateStoreTable() {
|
|
|
|
|
+ assertEquals(200, tables.add(token, code(null, 168L)).get("code"));
|
|
|
|
|
+ verify(codes).insertTableQrcode(any());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void ownerCannotCreateTableInAnotherOwnersStore() {
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> tables.add(JwtUtil.setToken("801", "owner"), code(null, 169L)));
|
|
|
|
|
+ verify(codes, never()).insertTableQrcode(any());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void authorizedSubaccountCanReadDetailsAndOrders() {
|
|
|
|
|
+ assertEquals(200, tables.getInfo(token, 70L).get("code"));
|
|
|
|
|
+ assertEquals(200, tables.getTableOrders(token, 70L, 1, 10, null).get("code"));
|
|
|
|
|
+ verify(orders).page(any(), any(Wrapper.class));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void detailChecksPersistedStore() {
|
|
|
|
|
+ table.setStoreId(169L);
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> tables.getInfo(token, 70L));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void authorizedSubaccountCanUpdateDeleteAndToggleTable() {
|
|
|
|
|
+ TableQrcode request = code(70L, 999L);
|
|
|
|
|
+ request.setStatus(1);
|
|
|
|
|
+ assertEquals(200, tables.update(token, request).get("code"));
|
|
|
|
|
+ assertEquals(200, tables.changeStatus(token, request).get("code"));
|
|
|
|
|
+ assertEquals(200, tables.delete(token, 70L).get("code"));
|
|
|
|
|
+ verify(codes, times(2)).updateTableQrcode(argThat(update -> update.getStoreId() == null));
|
|
|
|
|
+ verify(codes).deleteTableQrcodeById(70L);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void revokedGrantBlocksTableMutationUsingPersistedStore() {
|
|
|
|
|
+ grantsRevoked();
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> tables.update(token, code(70L, 168L)));
|
|
|
|
|
+ verify(codes, never()).updateTableQrcode(any());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void disabledSubaccountCannotReadTableList() {
|
|
|
|
|
+ subaccount.setSubaccountStatus("1");
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> tables.list(token, null, null, null, null));
|
|
|
|
|
+ verifyNoInteractions(codes);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void ordinaryCustomerCannotListAllTables() {
|
|
|
|
|
+ subaccount.setUserType("0");
|
|
|
|
|
+ assertEquals(500, tables.list(token, null, null, null, null).get("code"));
|
|
|
|
|
+ verifyNoInteractions(codes);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void grantsRevoked() {
|
|
|
|
|
+ subaccount.setStoreId(168L);
|
|
|
|
|
+ when(grants.selectStoreIdsBySubaccountUserId(936L)).thenReturn(List.of());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static InfoUser user(Long id, String type) {
|
|
|
|
|
+ InfoUser user = new InfoUser();
|
|
|
|
|
+ user.setUserId(id);
|
|
|
|
|
+ user.setUserType(type);
|
|
|
|
|
+ user.setStatus("0");
|
|
|
|
|
+ user.setDelFlag("0");
|
|
|
|
|
+ return user;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static TableQrcode code(Long id, Long storeId) {
|
|
|
|
|
+ TableQrcode table = new TableQrcode();
|
|
|
|
|
+ table.setId(id);
|
|
|
|
|
+ table.setStoreId(storeId);
|
|
|
|
|
+ table.setTableNo("A01");
|
|
|
|
|
+ table.setType(0);
|
|
|
|
|
+ table.setQrCode("existing");
|
|
|
|
|
+ return table;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|