UserServiceTest.java 12 KB
package com.example.erp.module.usr;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.erp.common.exception.BizException;
import com.example.erp.common.vo.PageVO;
import com.example.erp.config.UserPrincipal;
import com.example.erp.module.usr.dto.UserCreateReqDTO;
import com.example.erp.module.usr.dto.UserListQueryDTO;
import com.example.erp.module.usr.dto.UserUpdateReqDTO;
import com.example.erp.module.usr.vo.UserListItemVO;
import com.example.erp.module.usr.vo.UserUpdateRespVO;
import com.example.erp.module.usr.entity.StaffEntity;
import com.example.erp.module.usr.entity.UsrUserEntity;
import com.example.erp.module.usr.entity.UserPermissionEntity;
import com.example.erp.module.usr.mapper.PermissionGroupMapper;
import com.example.erp.module.usr.mapper.StaffMapper;
import com.example.erp.common.constants.UsrErrorCode;
import com.example.erp.module.usr.mapper.UserPermissionMapper;
import com.example.erp.module.usr.mapper.UsrUserMapper;
import com.example.erp.module.usr.service.impl.UserServiceImpl;
import com.example.erp.module.usr.vo.UserCreateRespVO;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock private UsrUserMapper userMapper;
    @Mock private StaffMapper staffMapper;
    @Mock private PermissionGroupMapper permGroupMapper;
    @Mock private UserPermissionMapper userPermissionMapper;
    @Mock private BCryptPasswordEncoder passwordEncoder;

    @InjectMocks
    private UserServiceImpl userService;

    private UserCreateReqDTO req;
    private UserPrincipal superAdmin;
    private UserPrincipal normalUser;

    @BeforeEach
    void setUp() {
        req = new UserCreateReqDTO();
        req.setUserCode("UC001");
        req.setUsername("testuser");
        req.setUserType("普通用户");
        req.setLanguage("中文");

        superAdmin = new UserPrincipal("u1", "admin", "超级管理员", "b1");
        normalUser = new UserPrincipal("u2", "user", "普通用户", "b1");
    }

    @Test
    void createUser_normalUser_throws40300() {
        BizException ex = assertThrows(BizException.class,
                () -> userService.createUser(req, normalUser));
        assertEquals(40300, ex.getCode());
    }

    @Test
    void createUser_success_insertsUserAndReturnsVO() {
        when(userMapper.selectCount(any())).thenReturn(0L);
        when(passwordEncoder.encode(anyString())).thenReturn("$2a$10$hashed");
        when(userMapper.insert(any(UsrUserEntity.class))).thenReturn(1);

        UserCreateRespVO vo = userService.createUser(req, superAdmin);

        assertNotNull(vo.getUserId());
        assertEquals("UC001", vo.getUserCode());
        assertEquals("testuser", vo.getUsername());
        verify(userMapper).insert(any(UsrUserEntity.class));
    }

    @Test
    void createUser_duplicateUserCode_throws40902() {
        when(userMapper.selectCount(any())).thenReturn(1L);

        BizException ex = assertThrows(BizException.class,
                () -> userService.createUser(req, superAdmin));
        assertEquals(40902, ex.getCode());
    }

    @Test
    void createUser_duplicateUsername_throws40901() {
        when(userMapper.selectCount(any())).thenReturn(0L, 1L);

        BizException ex = assertThrows(BizException.class,
                () -> userService.createUser(req, superAdmin));
        assertEquals(40901, ex.getCode());
    }

    @Test
    @SuppressWarnings("unchecked")
    void createUser_withPermGroups_insertsPermissions() {
        req.setPermGroupIds(List.of("g1", "g2"));
        when(userMapper.selectCount(any())).thenReturn(0L);
        when(passwordEncoder.encode(anyString())).thenReturn("$2a$10$hashed");
        when(userMapper.insert(any(UsrUserEntity.class))).thenReturn(1);

        userService.createUser(req, superAdmin);

        verify(userPermissionMapper).insert(argThat((java.util.Collection<UserPermissionEntity> c) -> c.size() == 2));
    }

    @Test
    void getUserList_queryDtoDefaults() {
        UserListQueryDTO q = new UserListQueryDTO();
        assertEquals("username", q.getQueryField());
        assertEquals("contains", q.getMatchType());
        assertEquals(20, q.getPageSize());
        assertEquals(1, q.getPage());
    }

    @Test
    void getUserList_capsPageSizeAt100_callsMapper() {
        IPage<UserListItemVO> mockPage = new Page<>(1, 100, 5);
        when(userMapper.selectUserList(argThat(p -> p.getSize() == 100), eq("b1"), any(), any(), any()))
                .thenReturn(mockPage);

        UserListQueryDTO q = new UserListQueryDTO();
        q.setPageSize(200);
        PageVO<UserListItemVO> result = userService.getUserList(q, "b1");

        assertEquals(5L, result.getTotal());
        verify(userMapper).selectUserList(argThat(p -> p.getSize() == 100), eq("b1"), any(), any(), any());
    }

    @Test
    void getUserList_emptyQueryValue_doesNotFilter() {
        IPage<UserListItemVO> mockPage = new Page<>(1, 20, 2);
        when(userMapper.selectUserList(any(), eq("b1"), eq("username"), eq("contains"), eq("")))
                .thenReturn(mockPage);

        UserListQueryDTO q = new UserListQueryDTO();
        q.setQueryValue(null);
        PageVO<UserListItemVO> result = userService.getUserList(q, "b1");

        assertEquals(2L, result.getTotal());
    }

    @Test
    void createUser_invalidEmployeeId_throws40001() {
        req.setEmployeeId("bad-staff-id");
        when(userMapper.selectCount(any())).thenReturn(0L);
        when(staffMapper.selectOne(any())).thenReturn(null);

        BizException ex = assertThrows(BizException.class,
                () -> userService.createUser(req, superAdmin));
        assertEquals(UsrErrorCode.EMPLOYEE_NOT_FOUND, ex.getCode());
    }

    @Test
    void updateUser_dtoDefaults() {
        com.example.erp.module.usr.dto.UserUpdateReqDTO dto = new com.example.erp.module.usr.dto.UserUpdateReqDTO();
        dto.setUserType("普通用户");
        dto.setLanguage("中文");
        dto.setCanEditDoc(false);
        dto.setDisabled(false);
        dto.setEmployeeId(null);
        dto.setPermGroupIds(List.of());

        assertEquals("普通用户", dto.getUserType());
        assertEquals("中文", dto.getLanguage());
        assertFalse(dto.isCanEditDoc());
        assertFalse(dto.isDisabled());
        assertNull(dto.getEmployeeId());
        assertTrue(dto.getPermGroupIds().isEmpty());

        com.example.erp.module.usr.vo.UserUpdateRespVO resp = new com.example.erp.module.usr.vo.UserUpdateRespVO();
        resp.setUserId("u1");
        resp.setUsername("alice");
        resp.setUpdatedAt(java.time.LocalDateTime.now());
        assertEquals("u1", resp.getUserId());
        assertEquals("alice", resp.getUsername());
        assertNotNull(resp.getUpdatedAt());

        assertEquals(40400, UsrErrorCode.USER_NOT_FOUND);
        assertEquals(40301, UsrErrorCode.SELF_ADMIN_CHANGE);
    }

    @Test
    void updateUser_nonAdmin_throws40300() {
        UserUpdateReqDTO dto = new UserUpdateReqDTO();
        dto.setUserType("普通用户");
        dto.setLanguage("中文");
        dto.setPermGroupIds(List.of());

        BizException ex = assertThrows(BizException.class,
                () -> userService.updateUser("u-target", dto, normalUser));
        assertEquals(UsrErrorCode.PERMISSION_DENIED, ex.getCode());
    }

    @Test
    void updateUser_userNotFound_throws40400() {
        UserUpdateReqDTO dto = new UserUpdateReqDTO();
        dto.setUserType("普通用户");
        dto.setLanguage("中文");
        dto.setPermGroupIds(List.of());
        when(userMapper.selectOne(any())).thenReturn(null);

        BizException ex = assertThrows(BizException.class,
                () -> userService.updateUser("u-target", dto, superAdmin));
        assertEquals(UsrErrorCode.USER_NOT_FOUND, ex.getCode());
    }

    @Test
    void updateUser_selfAdminChange_throws40301() {
        UserUpdateReqDTO dto = new UserUpdateReqDTO();
        dto.setUserType("普通用户");
        dto.setLanguage("中文");
        dto.setPermGroupIds(List.of());
        UsrUserEntity existing = new UsrUserEntity();
        existing.setSId("u1");
        existing.setSUsername("admin");
        when(userMapper.selectOne(any())).thenReturn(existing);

        // superAdmin.userId() == "u1", target userId == "u1" → self-admin change
        BizException ex = assertThrows(BizException.class,
                () -> userService.updateUser("u1", dto, superAdmin));
        assertEquals(UsrErrorCode.SELF_ADMIN_CHANGE, ex.getCode());
    }

    @Test
    void updateUser_selfAdminKeepType_doesNotThrow() {
        UserUpdateReqDTO dto = new UserUpdateReqDTO();
        dto.setUserType("超级管理员");
        dto.setLanguage("中文");
        dto.setPermGroupIds(List.of());
        UsrUserEntity existing = new UsrUserEntity();
        existing.setSId("u1");
        existing.setSUsername("admin");
        when(userMapper.selectOne(any())).thenReturn(existing);
        when(userMapper.update(any(), any())).thenReturn(1);

        // 自己保持 超级管理员 角色 → 不应抛 40301
        assertDoesNotThrow(() -> userService.updateUser("u1", dto, superAdmin));
    }

    @Test
    void updateUser_invalidEmployee_throws40001() {
        UserUpdateReqDTO dto = new UserUpdateReqDTO();
        dto.setUserType("普通用户");
        dto.setLanguage("中文");
        dto.setEmployeeId("bad-emp");
        dto.setPermGroupIds(List.of());
        UsrUserEntity existing = new UsrUserEntity();
        existing.setSId("u-target");
        existing.setSUsername("alice");
        when(userMapper.selectOne(any())).thenReturn(existing);
        when(staffMapper.selectOne(any())).thenReturn(null);

        BizException ex = assertThrows(BizException.class,
                () -> userService.updateUser("u-target", dto, superAdmin));
        assertEquals(UsrErrorCode.EMPLOYEE_NOT_FOUND, ex.getCode());
    }

    @Test
    void updateUser_happyPath_updatesAndReturnsResp() {
        UserUpdateReqDTO dto = new UserUpdateReqDTO();
        dto.setUserType("超级管理员");
        dto.setLanguage("英文");
        dto.setCanEditDoc(true);
        dto.setDisabled(false);
        dto.setPermGroupIds(List.of("g1", "g2"));
        UsrUserEntity existing = new UsrUserEntity();
        existing.setSId("u-target");
        existing.setSUsername("alice");
        when(userMapper.selectOne(any())).thenReturn(existing);
        when(userMapper.update(any(), any())).thenReturn(1);
        when(userPermissionMapper.delete(any())).thenReturn(0);

        UserUpdateRespVO resp = userService.updateUser("u-target", dto, superAdmin);

        assertNotNull(resp);
        assertEquals("u-target", resp.getUserId());
        assertEquals("alice", resp.getUsername());
        assertNotNull(resp.getUpdatedAt());
        verify(userMapper).update(any(), any());
        verify(userPermissionMapper).delete(any());
        verify(userPermissionMapper).insert(argThat((java.util.Collection<UserPermissionEntity> c) -> c.size() == 2));
    }

    @Test
    void updateUser_emptyPermGroupIds_onlyDeletes() {
        UserUpdateReqDTO dto = new UserUpdateReqDTO();
        dto.setUserType("普通用户");
        dto.setLanguage("中文");
        dto.setPermGroupIds(List.of());
        UsrUserEntity existing = new UsrUserEntity();
        existing.setSId("u-target");
        existing.setSUsername("bob");
        when(userMapper.selectOne(any())).thenReturn(existing);
        when(userMapper.update(any(), any())).thenReturn(1);
        when(userPermissionMapper.delete(any())).thenReturn(0);

        userService.updateUser("u-target", dto, superAdmin);

        verify(userPermissionMapper).delete(any());
        verify(userPermissionMapper, never()).insert(anyList());
    }
}