package com.cf.imes.module.system.service.permission;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.security.test.WithMockLoginUser;
import com.cf.imes.framework.test.core.ut.BaseMockitoUnitTest;
import com.cf.imes.module.system.common.WithMockLoginUserExtension;
import com.cf.imes.module.system.controller.admin.permission.vo.menu.MenuListReqVO;
import com.cf.imes.module.system.controller.admin.permission.vo.menu.MenuSaveVO;
import com.cf.imes.module.system.controller.admin.permission.vo.menu.MenuTransReqVO;
import com.cf.imes.module.system.dal.dataobject.permission.MenuDO;
import com.cf.imes.module.system.dal.dataobject.permission.MenuI18nDO;
import com.cf.imes.module.system.dal.mysql.permission.MenuI18nMapper;
import com.cf.imes.module.system.dal.mysql.permission.MenuMapper;
import com.cf.imes.module.system.enums.common.LanguageEnum;
import com.cf.imes.module.system.enums.permission.MenuSourceTypeEnum;
import com.cf.imes.module.system.enums.permission.MenuTypeEnum;
import com.cf.imes.module.system.framework.trans.config.TransApi;
import com.cf.imes.module.system.framework.trans.config.TransProperties;
import com.cf.imes.module.system.service.organ.OrganService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedConstruction;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import static com.cf.imes.framework.common.util.collection.SetUtils.asSet;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
import static com.cf.imes.framework.test.core.util.RandomUtils.*;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(WithMockLoginUserExtension.class)
class MenuServiceImplTest extends BaseMockitoUnitTest {

    @InjectMocks
    private MenuServiceImpl menuService;

    @Mock
    private MenuMapper menuMapper;

    @Mock
    private PermissionService permissionService;

    @Mock
    private OrganService organService;

    @Mock
    private RedisTemplate redisTemplate;

    @Mock
    private Cursor<String> cursor;

    @Mock
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Mock
    private MenuI18nMapper menuI18nMapper;

    @Mock
    private TransProperties transProperties;


    @Test
    @WithMockLoginUser(organId = 1L)
    void testCreateMenu_success() {
        // mock 数据（构造父菜单）
        MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU,
                "parent", 0L);
        Long parentId = menuDO.getId();
        when(menuMapper.selectById(parentId)).thenReturn(menuDO);

        // 校验自己
        when(menuMapper.selectByParentIdAndNameAndSourceType(any(), any(), any())).thenReturn(null);

        // 模拟 Mapper 插入时生成 ID
        doAnswer(invocation -> {
            MenuDO mdo = invocation.getArgument(0);
            mdo.setId(100L); // 模拟自增主键
            return null;
        }).when(menuMapper).insert(any(MenuDO.class));

        // 准备参数
        MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> {
            o.setParentId(parentId);
            o.setType(MenuTypeEnum.BUTTON.getType());
        }).setId(null); // 防止 id 被赋值

        // redis mock
        when(redisTemplate.scan(any())).thenReturn(cursor);
        when(cursor.hasNext()).thenReturn(true, false);

        // mock executor
        doAnswer((InvocationOnMock invocation) -> {
            ((Runnable) invocation.getArguments()[0]).run();
            return null;
        }).when(threadPoolTaskExecutor).execute(any(Runnable.class));

        Long menuId = menuService.createMenu(reqVO);

        // 断言
        assertEquals(100L, menuId);
        verify(redisTemplate, times(1)).convertAndSend(com.cf.imes.framework.redis.constants.RedisKeyConstants.PERMISSION_REFRESH, "");
        verify(redisTemplate, times(2)).scan(any());
        verify(menuMapper).insert(argThat((MenuDO menuArg) ->
                ObjectUtil.equals(menuArg.getName(), reqVO.getName())
                        && ObjectUtil.equals(menuArg.getPermission(), reqVO.getPermission())
                        && ObjectUtil.equals(menuArg.getType(), reqVO.getType())
                        && ObjectUtil.equals(menuArg.getSort(), reqVO.getSort())
                        && ObjectUtil.equals(menuArg.getParentId(), reqVO.getParentId())
                        && ObjectUtil.equals("", menuArg.getPath())
                        && ObjectUtil.equals("", menuArg.getIcon())
                        && ObjectUtil.equals("", menuArg.getComponent())
                        && ObjectUtil.equals("", menuArg.getComponentName())
                        && ObjectUtil.equals(menuArg.getStatus(), reqVO.getStatus())
                        && ObjectUtil.equals(menuArg.getVisible(), reqVO.getVisible())
                        && ObjectUtil.equals(menuArg.getKeepAlive(), reqVO.getKeepAlive())
                        && ObjectUtil.equals(menuArg.getAlwaysShow(), reqVO.getAlwaysShow())
                        && ObjectUtil.equals(menuArg.getDescription(), reqVO.getDescription())
                        && ObjectUtil.equals(menuArg.getSourceType(), reqVO.getSourceType())
        ));
    }

    @Test
    @WithMockLoginUser
    void testCreateMenu_fail_sourceTypePermissionError() {
        // mock 数据（构造父菜单）
        MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU,
                "parent", 0L);
        Long parentId = menuDO.getId();
        when(menuMapper.selectById(parentId)).thenReturn(menuDO);

        // 校验自己
        when(menuMapper.selectByParentIdAndNameAndSourceType(any(), any(), any())).thenReturn(null);

        // 准备参数
        MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> {
            o.setParentId(parentId);
            o.setSourceType(MenuSourceTypeEnum.MANAGEMENT.getType());
        }).setId(null); // 防止 id 被赋值

        assertServiceException(() -> menuService.createMenu(reqVO), MANAGEMENT_MENU_OPERATION_PERMISSION_ERROR);
    }

    @Test
    @WithMockLoginUser
    void testUpdateMenu_success() {
        // 校验父级
        MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU,
                "parent", 0L);

        // mock 数据（构造父子菜单）
        MenuDO sonMenuDO = buildMenuDO(MenuTypeEnum.MENU, "testSonName",
                menuDO.getParentId());
        when(menuMapper.selectById(any())).thenReturn(sonMenuDO).thenReturn(menuDO);
        when(menuMapper.selectByParentIdAndNameAndSourceType(any(), any(), any())).thenReturn(sonMenuDO);

        Long sonId = sonMenuDO.getId();
        // 准备参数
        MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> {
            o.setId(sonId);
            o.setName("testSonName"); // 修改名字
            o.setParentId(sonMenuDO.getParentId());
            o.setType(MenuTypeEnum.MENU.getType());
            o.setPermission(null);
        });

        // redis mock
        when(redisTemplate.scan(any())).thenReturn(cursor);
        when(cursor.hasNext()).thenReturn(true, false);

        // mock executor
        doAnswer((InvocationOnMock invocation) -> {
            ((Runnable) invocation.getArguments()[0]).run();
            return null;
        }).when(threadPoolTaskExecutor).execute(any(Runnable.class));


        // 调用
        menuService.updateMenu(reqVO);

        // 校验记录的属性是否正确
        verify(menuMapper, times(1)).updateById(any(MenuDO.class));
        verify(redisTemplate, times(2)).scan(any());
        verify(redisTemplate, times(1)).convertAndSend(com.cf.imes.framework.redis.constants.RedisKeyConstants.PERMISSION_REFRESH, "");
    }

    @Test
    @WithMockLoginUser
    void testUpdateMenu_success_flushCache() {
        // 校验父级
        MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU,
                "parent", 0L);

        // mock 数据（构造父子菜单）
        MenuDO sonMenuDO = buildMenuDO(MenuTypeEnum.MENU, "testSonName",
                menuDO.getParentId());
        // same permission
        sonMenuDO.setPermission("permission");

        when(menuMapper.selectById(any())).thenReturn(sonMenuDO).thenReturn(menuDO);
        when(menuMapper.selectByParentIdAndNameAndSourceType(any(), any(), any())).thenReturn(sonMenuDO);

        Long sonId = sonMenuDO.getId();
        // 准备参数
        MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> {
            o.setId(sonId);
            o.setName("testSonName"); // 修改名字
            o.setParentId(sonMenuDO.getParentId());
            o.setType(MenuTypeEnum.MENU.getType());
            // same permission
            o.setPermission("permission");
        });

        // 调用
        menuService.updateMenu(reqVO);

        // 校验记录的属性是否正确
        verify(menuMapper, times(1)).updateById(any(MenuDO.class));
        verify(redisTemplate, never()).scan(any());
        verify(redisTemplate, never()).convertAndSend(anyString(), anyString());
    }

    @Test
    void testUpdateMenu_sonIdNotExist() {
        // 准备参数
        MenuSaveVO reqVO = randomPojo(MenuSaveVO.class);
        // 调用，并断言异常
        assertServiceException(() -> menuService.updateMenu(reqVO), MENU_NOT_EXISTS);
    }

    @Test
    @WithMockLoginUser
    void testDeleteMenu_success() {
        // mock 数据
        MenuDO menuDO = randomPojo(MenuDO.class);
        // 准备参数
        Long id = menuDO.getId();
        when(menuMapper.selectCountByParentId(id)).thenReturn(0L);
        when(menuMapper.selectById(id)).thenReturn(menuDO);

        // 调用
        menuService.deleteMenu(id);
        // 断言
        verify(menuMapper, times(1)).deleteById(id);
        verify(permissionService, times(1)).processMenuDeleted(id);
    }

    @Test
    void testDeleteMenu_menuNotExist() {
        assertServiceException(() -> menuService.deleteMenu(randomLongId()),
                MENU_NOT_EXISTS);
    }

    @Test
    void testDeleteMenu_existChildren() {
        Long menuId = 100L;
        when(menuMapper.selectCountByParentId(menuId)).thenReturn(1L);

        // 调用并断言异常
        assertServiceException(() -> menuService.deleteMenu(menuId), MENU_EXISTS_CHILDREN);
    }

    @Test
    void testGetMenuList_all() {
        // mock 数据
        MenuDO menu100 = randomPojo(MenuDO.class);
        MenuDO menu101 = randomPojo(MenuDO.class);

        when(menuMapper.selectList()).thenReturn(List.of(menu100, menu101));

        // 调用
        List<MenuDO> list = menuService.getMenuList();
        // 断言
        assertEquals(2, list.size());
        assertPojoEquals(menu100, list.get(0));
        assertPojoEquals(menu101, list.get(1));
    }

    @Test
    void testGetMenuList() {
        // mock 数据
        MenuDO menuDO = randomPojo(MenuDO.class, o -> o.setName("晨丰").setStatus(CommonStatusEnum.ENABLE.getStatus()));

        // 准备参数
        MenuListReqVO reqVO = new MenuListReqVO().setName("芋").setStatus(CommonStatusEnum.ENABLE.getStatus());
        when(menuMapper.selectList(reqVO)).thenReturn(List.of(menuDO));

        // 调用
        List<MenuDO> result = menuService.getMenuList(reqVO);
        // 断言
        assertEquals(1, result.size());
        assertPojoEquals(menuDO, result.get(0));
    }

    @Test
    void testGetMenuListByTenant() {
        // mock 数据
        MenuDO menu100 = randomPojo(MenuDO.class, o -> o.setId(100L).setStatus(CommonStatusEnum.ENABLE.getStatus()));
        MenuDO menu101 = randomPojo(MenuDO.class, o -> o.setId(101L).setStatus(CommonStatusEnum.DISABLE.getStatus()));
        MenuDO menu102 = randomPojo(MenuDO.class, o -> o.setId(102L).setStatus(CommonStatusEnum.ENABLE.getStatus()));

        // mock 过滤菜单
        Set<Long> menuIds = asSet(100L);
        doNothing().when(organService).handleOrganMenu(argThat(handler -> {
            handler.handle(menuIds);
            return true;
        }));
        // 准备参数
        MenuListReqVO reqVO = new MenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus());
        when(menuMapper.selectList(reqVO)).thenReturn(new ArrayList<>(Arrays.asList(menu100, menu101, menu102)));

        // 调用
        List<MenuDO> result = menuService.getMenuListByOrgan(reqVO);
        // 断言
        assertEquals(1, result.size());
        assertPojoEquals(menu100, result.get(0));
    }

    @Test
    void testGetMenuList1_empty() {
        List<MenuDO> menuList1 = menuService.getMenuList1(Collections.emptyList());

        assertEquals(0, menuList1.size());
    }

    @Test
    void testGetMenuList1_success() {
        MenuDO menuDO = randomPojo(MenuDO.class);
        Set<Long> ids = Collections.singleton(menuDO.getId());
        when(menuMapper.selectByIds(ids)).thenReturn(List.of(menuDO));

        List<MenuDO> menuList1 = menuService.getMenuList1(ids);

        assertEquals(menuDO, menuList1.get(0));
    }

    @Test
    void testGetMenuIdListByPermissionFromCache() {
        // mock 数据
        MenuDO menu100 = randomPojo(MenuDO.class);
        // 准备参数
        String permission = menu100.getPermission();

        when(menuMapper.selectListByPermission(menu100.getPermission())).thenReturn(List.of(menu100));

        // 调用
        List<Long> ids = menuService.getMenuIdListByPermissionFromCache(permission);
        // 断言
        assertEquals(1, ids.size());
        assertEquals(menu100.getId(), ids.get(0));
    }

    @Test
    void testGetMenuList_ids() {
        // mock 数据
        MenuDO menu100 = randomPojo(MenuDO.class);

        // 准备参数
        Collection<Long> ids = Collections.singleton(menu100.getId());
        when(menuMapper.selectByIds(ids)).thenReturn(List.of(menu100));

        // 调用
        List<MenuDO> list = menuService.getMenuList(ids);
        // 断言
        assertEquals(1, list.size());
        assertPojoEquals(menu100, list.get(0));
    }

    @Test
    void testGetMenu() {
        // mock 数据
        MenuDO menu = randomPojo(MenuDO.class);

        // 准备参数
        Long id = menu.getId();
        when(menuMapper.selectById(id)).thenReturn(menu);

        // 调用
        MenuDO dbMenu = menuService.getMenu(id);
        // 断言
        assertPojoEquals(menu, dbMenu);
    }

    @Test
    void testValidateParentMenu_success() {
        // mock 数据
        MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", 0L);

        // 准备参数
        Long parentId = menuDO.getId();
        when(menuMapper.selectById(parentId)).thenReturn(menuDO);

        // 调用，无需断言
        menuService.validateParentMenu(parentId, null);
        menuService.validateParentMenu(null, null);
    }

    @Test
    void testValidateParentMenu_canNotSetSelfToBeParent() {
        // 调用，并断言异常
        assertServiceException(() -> menuService.validateParentMenu(1L, 1L),
                MENU_PARENT_ERROR);
    }

    @Test
    void testValidateParentMenu_parentNotExist() {
        // 调用，并断言异常
        assertServiceException(() -> menuService.validateParentMenu(randomLongId(), null),
                MENU_PARENT_NOT_EXISTS);
    }

    @Test
    void testValidateParentMenu_parentTypeError() {
        // mock 数据
        MenuDO menuDO = buildMenuDO(MenuTypeEnum.BUTTON, "parent", 0L);

        // 准备参数
        Long parentId = menuDO.getId();
        when(menuMapper.selectById(parentId)).thenReturn(menuDO);

        // 调用，并断言异常
        assertServiceException(() -> menuService.validateParentMenu(parentId, null),
                MENU_PARENT_NOT_DIR_OR_MENU);
    }

    @Test
    void testValidateMenu_success() {
        // mock 父子菜单
        MenuDO sonMenu = randomPojo(MenuDO.class);
        // 准备参数
        Long parentId = sonMenu.getParentId();
        Long otherSonMenuId = randomLongId();
        String otherSonMenuName = randomString();

        // 调用，无需断言
        menuService.validateMenu(parentId, otherSonMenuName, otherSonMenuId, MenuSourceTypeEnum.PRODUCTION.getType());
    }

    @Test
    void testValidateMenu_sonMenuNameDuplicate() {
        // mock 父子菜单
        MenuDO sonMenu = randomPojo(MenuDO.class);
        // 准备参数
        Long parentId = sonMenu.getParentId();
        Long otherSonMenuId = randomLongId();
        String otherSonMenuName = sonMenu.getName(); //相同名称

        when(menuMapper.selectByParentIdAndNameAndSourceType(parentId, otherSonMenuName, MenuSourceTypeEnum.PRODUCTION.getType())).thenReturn(sonMenu);

        // 调用，并断言异常
        assertServiceException(() -> menuService.validateMenu(parentId, otherSonMenuName, otherSonMenuId, MenuSourceTypeEnum.PRODUCTION.getType()),
                MENU_NAME_DUPLICATE);
    }

    @Test
    void testValidateMenu_sonMenuNameDuplicate_whenNull() {
        // mock 父子菜单
        MenuDO sonMenu = randomPojo(MenuDO.class);
        // 准备参数
        Long parentId = sonMenu.getParentId();
        String otherSonMenuName = sonMenu.getName(); //相同名称

        when(menuMapper.selectByParentIdAndNameAndSourceType(parentId, otherSonMenuName, MenuSourceTypeEnum.PRODUCTION.getType())).thenReturn(sonMenu);

        // 调用，并断言异常
        assertServiceException(() -> menuService.validateMenu(parentId, otherSonMenuName, null, MenuSourceTypeEnum.PRODUCTION.getType()),
                MENU_NAME_DUPLICATE);
    }


    @Test
    void testGetManageEndPointMenuList() {
        List<MenuDO> mockList = List.of(new MenuDO());

        when(menuMapper.selectListIn(
                anyCollection(),
                eq(true),
                anyString()
        )).thenReturn(mockList);

        List<MenuDO> result = menuService.getManageEndPointMenuList(List.of(1L, 2L));

        assertSame(mockList, result);

        verify(menuMapper).selectListIn(
                anyCollection(),
                eq(true),
                anyString()
        );
    }

    @Test
    void testGetCustomEndPointMenuList() {
        List<MenuDO> mockList = List.of(new MenuDO());

        when(menuMapper.selectListIn(
                anyCollection(),
                eq(false),
                anyString()
        )).thenReturn(mockList);

        List<MenuDO> result = menuService.getCustomEndPointMenuList(List.of(1L, 2L));

        assertSame(mockList, result);

        verify(menuMapper).selectListIn(
                anyCollection(),
                eq(false),
                anyString()
        );
    }

    // ====================== 初始化方法 ======================

    private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId) {
        return buildMenuDO(type, name, parentId, randomCommonStatus());
    }

    private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId, Integer status) {
        return randomPojo(MenuDO.class, o -> o.setName(name).setParentId(parentId)
                .setType(type.getType()).setStatus(status));
    }

    @Test
    void testTrans_errorCode() {
        // mock properties
        when(transProperties.getAppId()).thenReturn("appId");
        when(transProperties.getSecurityKey()).thenReturn("key");
        when(transProperties.getApiHost()).thenReturn("host");

        MenuTransReqVO req = new MenuTransReqVO();
        req.setTargetLanguage("en");

        when(menuMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(List.of(randomPojo(MenuDO.class)));

        String errorJson = """
                {
                  "error_code": "54001"
                }
                """;

        try (MockedConstruction<TransApi> mocked =
                     Mockito.mockConstruction(TransApi.class,
                             (mock, context) -> when(mock.getTransResult(any(), any(), any()))
                                     .thenReturn(errorJson))) {

            assertThrows(ServiceException.class,
                    () -> menuService.trans(req));
        }

        verify(menuI18nMapper, never()).insertBatch(any());
    }


    @Test
    void testTrans_success_en() {
        // mock properties
        when(transProperties.getAppId()).thenReturn("appId");
        when(transProperties.getSecurityKey()).thenReturn("key");
        when(transProperties.getApiHost()).thenReturn("host");

        MenuTransReqVO req = new MenuTransReqVO();
        req.setTargetLanguage("en");

        when(menuMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(List.of(randomPojo(MenuDO.class, o -> {
            o.setName("系统管理");
        })));

        String okJson = """
                {
                  "trans_result": [
                    { "src": "系统管理", "dst": "system management" }
                  ]
                }
                """;

        try (MockedConstruction<TransApi> mocked =
                     Mockito.mockConstruction(TransApi.class,
                             (mock, context) -> when(mock.getTransResult(any(), any(), any()))
                                     .thenReturn(okJson))) {

            menuService.trans(req);
        }

        ArgumentCaptor<List<MenuI18nDO>> captor =
                ArgumentCaptor.forClass(List.class);

        verify(menuI18nMapper).insertBatch(captor.capture());

        MenuI18nDO i18n = captor.getValue().get(0);
        assertEquals("System Management", i18n.getMenuName());
        assertEquals("en", i18n.getLocale());
    }

    @Test
    void testTrans_srcNotMatch() {
        MenuTransReqVO req = new MenuTransReqVO();
        req.setTargetLanguage("en");

        when(menuMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(List.of(randomPojo(MenuDO.class, o -> {
            o.setName("系统管理");
        })));

        String json = """
                {
                  "trans_result": [
                    { "src": "用户管理", "dst": "user management" }
                  ]
                }
                """;

        try (MockedConstruction<TransApi> mocked =
                     Mockito.mockConstruction(TransApi.class,
                             (mock, context) -> when(mock.getTransResult(any(), any(), any()))
                                     .thenReturn(json))) {

            menuService.trans(req);
        }

        verify(menuI18nMapper, never()).insertBatch(any());
    }

    @Test
    void testTrans_partialSuccess() {
        MenuTransReqVO req = new MenuTransReqVO();
        req.setTargetLanguage(LanguageEnum.ZH.getLanguage());

        when(menuMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(
                List.of(
                        randomPojo(MenuDO.class, o -> o.setName("系统管理")),
                        randomPojo(MenuDO.class, o -> o.setName("用户管理"))
                )
        );

        String json = """
                {
                  "trans_result": [
                    { "src": "系统管理", "dst": "system management" },
                    { "src": "角色管理", "dst": "role management" }
                  ]
                }
                """;

        try (MockedConstruction<TransApi> mocked =
                     Mockito.mockConstruction(TransApi.class,
                             (mock, context) -> when(mock.getTransResult(any(), any(), any()))
                                     .thenReturn(json))) {
            menuService.trans(req);
        }

        ArgumentCaptor<List<MenuI18nDO>> captor =
                ArgumentCaptor.forClass(List.class);

        verify(menuI18nMapper).insertBatch(captor.capture());
        assertEquals(1, captor.getValue().size());
    }
}
