From 25de8ad1b77134facfee9341c9ec1b9b100ad13c Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 02:08:08 +0000 Subject: [PATCH] CodeRabbit Generated Unit Tests: Add unit tests for service implementations --- .../service/impl/AddressServiceImplTest.java | 356 +++++++++++++++ .../impl/PermissionServiceImplTest.java | 422 ++++++++++++++++++ .../impl/TransactionServiceImplTest.java | 404 +++++++++++++++++ 3 files changed, 1182 insertions(+) create mode 100644 src/test/java/iuh/fit/se/ecommerce/service/impl/AddressServiceImplTest.java create mode 100644 src/test/java/iuh/fit/se/ecommerce/service/impl/PermissionServiceImplTest.java create mode 100644 src/test/java/iuh/fit/se/ecommerce/service/impl/TransactionServiceImplTest.java diff --git a/src/test/java/iuh/fit/se/ecommerce/service/impl/AddressServiceImplTest.java b/src/test/java/iuh/fit/se/ecommerce/service/impl/AddressServiceImplTest.java new file mode 100644 index 0000000..b5255a8 --- /dev/null +++ b/src/test/java/iuh/fit/se/ecommerce/service/impl/AddressServiceImplTest.java @@ -0,0 +1,356 @@ +package iuh.fit.se.ecommerce.service.impl; + +import iuh.fit.se.ecommerce.dto.request.AddressRequest; +import iuh.fit.se.ecommerce.dto.response.AddressResponse; +import iuh.fit.se.ecommerce.entity.Address; +import iuh.fit.se.ecommerce.entity.User; +import iuh.fit.se.ecommerce.exception.AppException; +import iuh.fit.se.ecommerce.exception.ErrorCode; +import iuh.fit.se.ecommerce.repository.AddressRepository; +import iuh.fit.se.ecommerce.repository.UserRepository; +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 java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class AddressServiceImplTest { + + @Mock + private AddressRepository addressRepository; + + @Mock + private UserRepository userRepository; + + @InjectMocks + private AddressServiceImpl addressService; + + private User testUser; + private Address testAddress; + private AddressRequest addressRequest; + + @BeforeEach + void setUp() { + testUser = User.builder() + .id(1L) + .email("test@example.com") + .fullName("Test User") + .build(); + + testAddress = Address.builder() + .id(1L) + .label("Home") + .receiverName("John Doe") + .receiverPhone("0123456789") + .province("Ho Chi Minh") + .ward("Ward 1") + .detail("123 Main St") + .isDefault(false) + .latitude(10.762622) + .longitude(106.660172) + .user(testUser) + .build(); + + addressRequest = AddressRequest.builder() + .label("Office") + .receiverName("Jane Doe") + .receiverPhone("0987654321") + .province("Hanoi") + .ward("Ward 2") + .detail("456 Second St") + .isDefault(false) + .latitude(21.028511) + .longitude(105.804817) + .build(); + } + + @Test + void getUserAddresses_Success() { + // Given + Address address2 = Address.builder() + .id(2L) + .label("Office") + .receiverName("Jane Doe") + .receiverPhone("0987654321") + .province("Hanoi") + .ward("Ward 2") + .detail("456 Second St") + .isDefault(true) + .user(testUser) + .build(); + + when(userRepository.findByEmail("test@example.com")).thenReturn(Optional.of(testUser)); + when(addressRepository.findByUserOrderByIsDefaultDescCreatedAtDesc(testUser)) + .thenReturn(Arrays.asList(address2, testAddress)); + + // When + List result = addressService.getUserAddresses("test@example.com"); + + // Then + assertThat(result).hasSize(2); + assertThat(result.get(0).getLabel()).isEqualTo("Office"); + assertThat(result.get(0).getIsDefault()).isTrue(); + assertThat(result.get(1).getLabel()).isEqualTo("Home"); + assertThat(result.get(1).getIsDefault()).isFalse(); + verify(userRepository).findByEmail("test@example.com"); + verify(addressRepository).findByUserOrderByIsDefaultDescCreatedAtDesc(testUser); + } + + @Test + void getUserAddresses_UserNotFound_ThrowsException() { + // Given + when(userRepository.findByEmail("nonexistent@example.com")).thenReturn(Optional.empty()); + + // When & Then + assertThatThrownBy(() -> addressService.getUserAddresses("nonexistent@example.com")) + .isInstanceOf(AppException.class) + .hasFieldOrPropertyWithValue("errorCode", ErrorCode.USER_NOT_FOUND); + verify(userRepository).findByEmail("nonexistent@example.com"); + verify(addressRepository, never()).findByUserOrderByIsDefaultDescCreatedAtDesc(any()); + } + + @Test + void getAddressById_Success() { + // Given + when(userRepository.findByEmail("test@example.com")).thenReturn(Optional.of(testUser)); + when(addressRepository.findByIdAndUser(1L, testUser)).thenReturn(Optional.of(testAddress)); + + // When + AddressResponse result = addressService.getAddressById(1L, "test@example.com"); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getId()).isEqualTo(1L); + assertThat(result.getLabel()).isEqualTo("Home"); + assertThat(result.getReceiverName()).isEqualTo("John Doe"); + assertThat(result.getProvince()).isEqualTo("Ho Chi Minh"); + verify(userRepository).findByEmail("test@example.com"); + verify(addressRepository).findByIdAndUser(1L, testUser); + } + + @Test + void getAddressById_AddressNotFound_ThrowsException() { + // Given + when(userRepository.findByEmail("test@example.com")).thenReturn(Optional.of(testUser)); + when(addressRepository.findByIdAndUser(999L, testUser)).thenReturn(Optional.empty()); + + // When & Then + assertThatThrownBy(() -> addressService.getAddressById(999L, "test@example.com")) + .isInstanceOf(AppException.class) + .hasMessageContaining("Địa chỉ không tồn tại"); + verify(userRepository).findByEmail("test@example.com"); + verify(addressRepository).findByIdAndUser(999L, testUser); + } + + @Test + void createAddress_Success() { + // Given + when(userRepository.findByEmail("test@example.com")).thenReturn(Optional.of(testUser)); + when(addressRepository.save(any(Address.class))).thenAnswer(invocation -> { + Address saved = invocation.getArgument(0); + saved.setId(2L); + return saved; + }); + + // When + AddressResponse result = addressService.createAddress(addressRequest, "test@example.com"); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getLabel()).isEqualTo("Office"); + assertThat(result.getReceiverName()).isEqualTo("Jane Doe"); + assertThat(result.getProvince()).isEqualTo("Hanoi"); + assertThat(result.getLatitude()).isEqualTo(21.028511); + assertThat(result.getLongitude()).isEqualTo(105.804817); + verify(userRepository).findByEmail("test@example.com"); + verify(addressRepository).save(any(Address.class)); + } + + @Test + void createAddress_WithDefault_UnsetsOtherDefaults() { + // Given + Address existingDefaultAddress = Address.builder() + .id(3L) + .isDefault(true) + .user(testUser) + .build(); + + addressRequest.setIsDefault(true); + + when(userRepository.findByEmail("test@example.com")).thenReturn(Optional.of(testUser)); + when(addressRepository.findByUserAndIsDefaultTrue(testUser)) + .thenReturn(Optional.of(existingDefaultAddress)); + when(addressRepository.save(any(Address.class))).thenAnswer(invocation -> invocation.getArgument(0)); + + // When + AddressResponse result = addressService.createAddress(addressRequest, "test@example.com"); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getIsDefault()).isTrue(); + verify(addressRepository, times(2)).save(any(Address.class)); // Once for unset, once for new + assertThat(existingDefaultAddress.isDefault()).isFalse(); + } + + @Test + void createAddress_NullCoordinates_Success() { + // Given + addressRequest.setLatitude(null); + addressRequest.setLongitude(null); + + when(userRepository.findByEmail("test@example.com")).thenReturn(Optional.of(testUser)); + when(addressRepository.save(any(Address.class))).thenAnswer(invocation -> { + Address saved = invocation.getArgument(0); + saved.setId(2L); + return saved; + }); + + // When + AddressResponse result = addressService.createAddress(addressRequest, "test@example.com"); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getLatitude()).isNull(); + assertThat(result.getLongitude()).isNull(); + verify(addressRepository).save(any(Address.class)); + } + + @Test + void updateAddress_Success() { + // Given + when(userRepository.findByEmail("test@example.com")).thenReturn(Optional.of(testUser)); + when(addressRepository.findByIdAndUser(1L, testUser)).thenReturn(Optional.of(testAddress)); + when(addressRepository.save(any(Address.class))).thenAnswer(invocation -> invocation.getArgument(0)); + + // When + AddressResponse result = addressService.updateAddress(1L, addressRequest, "test@example.com"); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getLabel()).isEqualTo("Office"); + assertThat(result.getReceiverName()).isEqualTo("Jane Doe"); + assertThat(result.getProvince()).isEqualTo("Hanoi"); + verify(addressRepository).save(testAddress); + } + + @Test + void updateAddress_SetAsDefault_UnsetsOtherDefaults() { + // Given + Address existingDefaultAddress = Address.builder() + .id(3L) + .isDefault(true) + .user(testUser) + .build(); + + addressRequest.setIsDefault(true); + + when(userRepository.findByEmail("test@example.com")).thenReturn(Optional.of(testUser)); + when(addressRepository.findByIdAndUser(1L, testUser)).thenReturn(Optional.of(testAddress)); + when(addressRepository.findByUserAndIsDefaultTrue(testUser)) + .thenReturn(Optional.of(existingDefaultAddress)); + when(addressRepository.save(any(Address.class))).thenAnswer(invocation -> invocation.getArgument(0)); + + // When + AddressResponse result = addressService.updateAddress(1L, addressRequest, "test@example.com"); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getIsDefault()).isTrue(); + assertThat(existingDefaultAddress.isDefault()).isFalse(); + verify(addressRepository, times(2)).save(any(Address.class)); + } + + @Test + void updateAddress_AddressNotFound_ThrowsException() { + // Given + when(userRepository.findByEmail("test@example.com")).thenReturn(Optional.of(testUser)); + when(addressRepository.findByIdAndUser(999L, testUser)).thenReturn(Optional.empty()); + + // When & Then + assertThatThrownBy(() -> addressService.updateAddress(999L, addressRequest, "test@example.com")) + .isInstanceOf(AppException.class) + .hasMessageContaining("Địa chỉ không tồn tại"); + } + + @Test + void deleteAddress_Success() { + // Given + when(userRepository.findByEmail("test@example.com")).thenReturn(Optional.of(testUser)); + when(addressRepository.findByIdAndUser(1L, testUser)).thenReturn(Optional.of(testAddress)); + doNothing().when(addressRepository).delete(testAddress); + + // When + addressService.deleteAddress(1L, "test@example.com"); + + // Then + verify(addressRepository).delete(testAddress); + } + + @Test + void deleteAddress_AddressNotFound_ThrowsException() { + // Given + when(userRepository.findByEmail("test@example.com")).thenReturn(Optional.of(testUser)); + when(addressRepository.findByIdAndUser(999L, testUser)).thenReturn(Optional.empty()); + + // When & Then + assertThatThrownBy(() -> addressService.deleteAddress(999L, "test@example.com")) + .isInstanceOf(AppException.class) + .hasMessageContaining("Địa chỉ không tồn tại"); + verify(addressRepository, never()).delete(any()); + } + + @Test + void setDefaultAddress_Success() { + // Given + Address existingDefaultAddress = Address.builder() + .id(3L) + .isDefault(true) + .user(testUser) + .build(); + + when(userRepository.findByEmail("test@example.com")).thenReturn(Optional.of(testUser)); + when(addressRepository.findByIdAndUser(1L, testUser)).thenReturn(Optional.of(testAddress)); + when(addressRepository.findByUserAndIsDefaultTrue(testUser)) + .thenReturn(Optional.of(existingDefaultAddress)); + when(addressRepository.save(any(Address.class))).thenAnswer(invocation -> invocation.getArgument(0)); + + // When + AddressResponse result = addressService.setDefaultAddress(1L, "test@example.com"); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getIsDefault()).isTrue(); + assertThat(testAddress.isDefault()).isTrue(); + assertThat(existingDefaultAddress.isDefault()).isFalse(); + verify(addressRepository, times(2)).save(any(Address.class)); + } + + @Test + void setDefaultAddress_NoExistingDefault_Success() { + // Given + when(userRepository.findByEmail("test@example.com")).thenReturn(Optional.of(testUser)); + when(addressRepository.findByIdAndUser(1L, testUser)).thenReturn(Optional.of(testAddress)); + when(addressRepository.findByUserAndIsDefaultTrue(testUser)).thenReturn(Optional.empty()); + when(addressRepository.save(any(Address.class))).thenAnswer(invocation -> invocation.getArgument(0)); + + // When + AddressResponse result = addressService.setDefaultAddress(1L, "test@example.com"); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getIsDefault()).isTrue(); + verify(addressRepository, times(1)).save(testAddress); + } +} \ No newline at end of file diff --git a/src/test/java/iuh/fit/se/ecommerce/service/impl/PermissionServiceImplTest.java b/src/test/java/iuh/fit/se/ecommerce/service/impl/PermissionServiceImplTest.java new file mode 100644 index 0000000..bf0a90e --- /dev/null +++ b/src/test/java/iuh/fit/se/ecommerce/service/impl/PermissionServiceImplTest.java @@ -0,0 +1,422 @@ +package iuh.fit.se.ecommerce.service.impl; + +import iuh.fit.se.ecommerce.dto.mapper.PermissionMapper; +import iuh.fit.se.ecommerce.dto.response.PermissionResponse; +import iuh.fit.se.ecommerce.dto.response.RolePermissionResponse; +import iuh.fit.se.ecommerce.entity.Permission; +import iuh.fit.se.ecommerce.entity.RolePermission; +import iuh.fit.se.ecommerce.entity.User; +import iuh.fit.se.ecommerce.entity.enums.Role; +import iuh.fit.se.ecommerce.exception.AppException; +import iuh.fit.se.ecommerce.exception.ErrorCode; +import iuh.fit.se.ecommerce.repository.PermissionRepository; +import iuh.fit.se.ecommerce.repository.RolePermissionRepository; +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 java.util.*; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class PermissionServiceImplTest { + + @Mock + private PermissionRepository permissionRepository; + + @Mock + private RolePermissionRepository rolePermissionRepository; + + @Mock + private PermissionMapper permissionMapper; + + @InjectMocks + private PermissionServiceImpl permissionService; + + private User testUser; + private Permission permission1; + private Permission permission2; + private Permission permission3; + + @BeforeEach + void setUp() { + testUser = User.builder() + .id(1L) + .email("admin@example.com") + .fullName("Admin User") + .role(Role.ADMIN) + .build(); + + permission1 = Permission.builder() + .id(1L) + .code("PRODUCT_CREATE") + .name("Create Product") + .description("Permission to create products") + .resource("PRODUCT") + .action("CREATE") + .build(); + + permission2 = Permission.builder() + .id(2L) + .code("PRODUCT_VIEW") + .name("View Product") + .description("Permission to view products") + .resource("PRODUCT") + .action("VIEW") + .build(); + + permission3 = Permission.builder() + .id(3L) + .code("ORDER_UPDATE") + .name("Update Order") + .description("Permission to update orders") + .resource("ORDER") + .action("UPDATE") + .build(); + } + + @Test + void hasPermission_UserHasPermission_ReturnsTrue() { + // Given + when(permissionRepository.findByRole(Role.ADMIN)) + .thenReturn(new HashSet<>(Arrays.asList(permission1, permission2))); + + // When + boolean result = permissionService.hasPermission(testUser, "PRODUCT_CREATE"); + + // Then + assertThat(result).isTrue(); + } + + @Test + void hasPermission_UserDoesNotHavePermission_ReturnsFalse() { + // Given + when(permissionRepository.findByRole(Role.ADMIN)) + .thenReturn(new HashSet<>(Arrays.asList(permission2))); + + // When + boolean result = permissionService.hasPermission(testUser, "PRODUCT_CREATE"); + + // Then + assertThat(result).isFalse(); + } + + @Test + void hasPermission_NullUser_ReturnsFalse() { + // When + boolean result = permissionService.hasPermission(null, "PRODUCT_CREATE"); + + // Then + assertThat(result).isFalse(); + verify(permissionRepository, never()).findByRole(any()); + } + + @Test + void hasPermission_UserWithNullRole_ReturnsFalse() { + // Given + testUser.setRole(null); + + // When + boolean result = permissionService.hasPermission(testUser, "PRODUCT_CREATE"); + + // Then + assertThat(result).isFalse(); + verify(permissionRepository, never()).findByRole(any()); + } + + @Test + void hasAnyPermission_UserHasOnePermission_ReturnsTrue() { + // Given + when(permissionRepository.findByRole(Role.ADMIN)) + .thenReturn(new HashSet<>(Arrays.asList(permission1, permission2))); + + // When + boolean result = permissionService.hasAnyPermission(testUser, "PRODUCT_CREATE", "ORDER_DELETE"); + + // Then + assertThat(result).isTrue(); + } + + @Test + void hasAnyPermission_UserHasNoPermissions_ReturnsFalse() { + // Given + when(permissionRepository.findByRole(Role.ADMIN)) + .thenReturn(new HashSet<>(Arrays.asList(permission3))); + + // When + boolean result = permissionService.hasAnyPermission(testUser, "PRODUCT_CREATE", "PRODUCT_VIEW"); + + // Then + assertThat(result).isFalse(); + } + + @Test + void hasAnyPermission_NullUser_ReturnsFalse() { + // When + boolean result = permissionService.hasAnyPermission(null, "PRODUCT_CREATE"); + + // Then + assertThat(result).isFalse(); + } + + @Test + void hasAllPermissions_UserHasAllPermissions_ReturnsTrue() { + // Given + when(permissionRepository.findByRole(Role.ADMIN)) + .thenReturn(new HashSet<>(Arrays.asList(permission1, permission2, permission3))); + + // When + boolean result = permissionService.hasAllPermissions(testUser, "PRODUCT_CREATE", "PRODUCT_VIEW"); + + // Then + assertThat(result).isTrue(); + } + + @Test + void hasAllPermissions_UserMissingOnePermission_ReturnsFalse() { + // Given + when(permissionRepository.findByRole(Role.ADMIN)) + .thenReturn(new HashSet<>(Arrays.asList(permission1))); + + // When + boolean result = permissionService.hasAllPermissions(testUser, "PRODUCT_CREATE", "PRODUCT_VIEW"); + + // Then + assertThat(result).isFalse(); + } + + @Test + void hasAllPermissions_NullUser_ReturnsFalse() { + // When + boolean result = permissionService.hasAllPermissions(null, "PRODUCT_CREATE"); + + // Then + assertThat(result).isFalse(); + } + + @Test + void getPermissionsByRole_Success() { + // Given + Set permissions = new HashSet<>(Arrays.asList(permission1, permission2)); + when(permissionRepository.findByRole(Role.ADMIN)).thenReturn(permissions); + + // When + Set result = permissionService.getPermissionsByRole(Role.ADMIN); + + // Then + assertThat(result).hasSize(2); + assertThat(result).contains(permission1, permission2); + verify(permissionRepository).findByRole(Role.ADMIN); + } + + @Test + void getPermissionCodesByRole_Success() { + // Given + Set permissions = new HashSet<>(Arrays.asList(permission1, permission2)); + when(permissionRepository.findByRole(Role.ADMIN)).thenReturn(permissions); + + // When + Set result = permissionService.getPermissionCodesByRole(Role.ADMIN); + + // Then + assertThat(result).hasSize(2); + assertThat(result).contains("PRODUCT_CREATE", "PRODUCT_VIEW"); + verify(permissionRepository).findByRole(Role.ADMIN); + } + + @Test + void assignPermissionToRole_NewAssignment_Success() { + // Given + when(permissionRepository.findByCode("PRODUCT_CREATE")).thenReturn(Optional.of(permission1)); + when(rolePermissionRepository.existsByRoleAndPermission(Role.STAFF, permission1)).thenReturn(false); + when(rolePermissionRepository.save(any(RolePermission.class))).thenAnswer(invocation -> invocation.getArgument(0)); + + // When + permissionService.assignPermissionToRole(Role.STAFF, "PRODUCT_CREATE"); + + // Then + verify(permissionRepository).findByCode("PRODUCT_CREATE"); + verify(rolePermissionRepository).existsByRoleAndPermission(Role.STAFF, permission1); + verify(rolePermissionRepository).save(any(RolePermission.class)); + } + + @Test + void assignPermissionToRole_AlreadyAssigned_DoesNotDuplicate() { + // Given + when(permissionRepository.findByCode("PRODUCT_CREATE")).thenReturn(Optional.of(permission1)); + when(rolePermissionRepository.existsByRoleAndPermission(Role.STAFF, permission1)).thenReturn(true); + + // When + permissionService.assignPermissionToRole(Role.STAFF, "PRODUCT_CREATE"); + + // Then + verify(permissionRepository).findByCode("PRODUCT_CREATE"); + verify(rolePermissionRepository).existsByRoleAndPermission(Role.STAFF, permission1); + verify(rolePermissionRepository, never()).save(any()); + } + + @Test + void assignPermissionToRole_PermissionNotFound_ThrowsException() { + // Given + when(permissionRepository.findByCode("INVALID_CODE")).thenReturn(Optional.empty()); + + // When & Then + assertThatThrownBy(() -> permissionService.assignPermissionToRole(Role.STAFF, "INVALID_CODE")) + .isInstanceOf(AppException.class) + .hasFieldOrPropertyWithValue("errorCode", ErrorCode.NOT_FOUND) + .hasMessageContaining("Permission not found"); + verify(rolePermissionRepository, never()).save(any()); + } + + @Test + void removePermissionFromRole_Success() { + // Given + RolePermission rolePermission = RolePermission.builder() + .id(1L) + .role(Role.STAFF) + .permission(permission1) + .build(); + + when(permissionRepository.findByCode("PRODUCT_CREATE")).thenReturn(Optional.of(permission1)); + when(rolePermissionRepository.findByRole(Role.STAFF)) + .thenReturn(Arrays.asList(rolePermission)); + + // When + permissionService.removePermissionFromRole(Role.STAFF, "PRODUCT_CREATE"); + + // Then + verify(permissionRepository).findByCode("PRODUCT_CREATE"); + verify(rolePermissionRepository).delete(rolePermission); + } + + @Test + void removePermissionFromRole_PermissionNotFound_ThrowsException() { + // Given + when(permissionRepository.findByCode("INVALID_CODE")).thenReturn(Optional.empty()); + + // When & Then + assertThatThrownBy(() -> permissionService.removePermissionFromRole(Role.STAFF, "INVALID_CODE")) + .isInstanceOf(AppException.class) + .hasFieldOrPropertyWithValue("errorCode", ErrorCode.NOT_FOUND); + verify(rolePermissionRepository, never()).delete(any()); + } + + @Test + void getAllPermissions_Success() { + // Given + List permissions = Arrays.asList(permission1, permission2, permission3); + PermissionResponse response1 = PermissionResponse.builder().code("PRODUCT_CREATE").build(); + PermissionResponse response2 = PermissionResponse.builder().code("PRODUCT_VIEW").build(); + PermissionResponse response3 = PermissionResponse.builder().code("ORDER_UPDATE").build(); + + when(permissionRepository.findAll()).thenReturn(permissions); + when(permissionMapper.toResponse(permission1)).thenReturn(response1); + when(permissionMapper.toResponse(permission2)).thenReturn(response2); + when(permissionMapper.toResponse(permission3)).thenReturn(response3); + + // When + List result = permissionService.getAllPermissions(); + + // Then + assertThat(result).hasSize(3); + verify(permissionRepository).findAll(); + } + + @Test + void getPermissionByCode_Success() { + // Given + PermissionResponse response = PermissionResponse.builder() + .code("PRODUCT_CREATE") + .name("Create Product") + .build(); + + when(permissionRepository.findByCode("PRODUCT_CREATE")).thenReturn(Optional.of(permission1)); + when(permissionMapper.toResponse(permission1)).thenReturn(response); + + // When + PermissionResponse result = permissionService.getPermissionByCode("PRODUCT_CREATE"); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getCode()).isEqualTo("PRODUCT_CREATE"); + verify(permissionRepository).findByCode("PRODUCT_CREATE"); + } + + @Test + void getPermissionByCode_NotFound_ThrowsException() { + // Given + when(permissionRepository.findByCode("INVALID_CODE")).thenReturn(Optional.empty()); + + // When & Then + assertThatThrownBy(() -> permissionService.getPermissionByCode("INVALID_CODE")) + .isInstanceOf(AppException.class) + .hasFieldOrPropertyWithValue("errorCode", ErrorCode.NOT_FOUND); + } + + @Test + void getAllRolesWithPermissions_Success() { + // Given + Set adminPermissions = new HashSet<>(Arrays.asList(permission1, permission2)); + Set staffPermissions = new HashSet<>(Arrays.asList(permission2)); + + when(permissionRepository.findByRole(Role.ADMIN)).thenReturn(adminPermissions); + when(permissionRepository.findByRole(Role.STAFF)).thenReturn(staffPermissions); + when(permissionRepository.findByRole(Role.CUSTOMER)).thenReturn(new HashSet<>()); + + when(permissionMapper.toResponse(any())).thenAnswer(invocation -> { + Permission p = invocation.getArgument(0); + return PermissionResponse.builder().code(p.getCode()).build(); + }); + + // When + List result = permissionService.getAllRolesWithPermissions(); + + // Then + assertThat(result).hasSizeGreaterThanOrEqualTo(3); + } + + @Test + void getRoleWithPermissions_Success() { + // Given + Set permissions = new HashSet<>(Arrays.asList(permission1, permission2)); + PermissionResponse response1 = PermissionResponse.builder().code("PRODUCT_CREATE").build(); + PermissionResponse response2 = PermissionResponse.builder().code("PRODUCT_VIEW").build(); + + when(permissionRepository.findByRole(Role.ADMIN)).thenReturn(permissions); + when(permissionMapper.toResponse(permission1)).thenReturn(response1); + when(permissionMapper.toResponse(permission2)).thenReturn(response2); + + // When + RolePermissionResponse result = permissionService.getRoleWithPermissions(Role.ADMIN); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getRoleName()).isEqualTo("ADMIN"); + assertThat(result.getRoleCode()).isEqualTo("ADMIN"); + assertThat(result.getPermissions()).hasSize(2); + } + + @Test + void getRolesByPermission_Success() { + // Given + Set roles = new HashSet<>(Arrays.asList(Role.ADMIN, Role.STAFF)); + + when(rolePermissionRepository.findRolesByPermissionCode("PRODUCT_VIEW")).thenReturn(roles); + when(permissionRepository.findByRole(any())).thenReturn(new HashSet<>()); + + // When + List result = permissionService.getRolesByPermission("PRODUCT_VIEW"); + + // Then + assertThat(result).hasSize(2); + verify(rolePermissionRepository).findRolesByPermissionCode("PRODUCT_VIEW"); + } +} \ No newline at end of file diff --git a/src/test/java/iuh/fit/se/ecommerce/service/impl/TransactionServiceImplTest.java b/src/test/java/iuh/fit/se/ecommerce/service/impl/TransactionServiceImplTest.java new file mode 100644 index 0000000..5dc3609 --- /dev/null +++ b/src/test/java/iuh/fit/se/ecommerce/service/impl/TransactionServiceImplTest.java @@ -0,0 +1,404 @@ +package iuh.fit.se.ecommerce.service.impl; + +import iuh.fit.se.ecommerce.dto.response.TransactionResponse; +import iuh.fit.se.ecommerce.dto.response.TransactionSummaryResponse; +import iuh.fit.se.ecommerce.entity.Order; +import iuh.fit.se.ecommerce.entity.Payment; +import iuh.fit.se.ecommerce.entity.Transaction; +import iuh.fit.se.ecommerce.entity.User; +import iuh.fit.se.ecommerce.entity.enums.PaymentMethod; +import iuh.fit.se.ecommerce.entity.enums.TransactionStatus; +import iuh.fit.se.ecommerce.entity.enums.TransactionType; +import iuh.fit.se.ecommerce.exception.AppException; +import iuh.fit.se.ecommerce.exception.ErrorCode; +import iuh.fit.se.ecommerce.repository.PaymentRepository; +import iuh.fit.se.ecommerce.repository.TransactionRepository; +import iuh.fit.se.ecommerce.repository.UserRepository; +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.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class TransactionServiceImplTest { + + @Mock + private TransactionRepository transactionRepository; + + @Mock + private UserRepository userRepository; + + @Mock + private PaymentRepository paymentRepository; + + @InjectMocks + private TransactionServiceImpl transactionService; + + private User testUser; + private Order testOrder; + private Payment testPayment; + private Transaction testTransaction; + + @BeforeEach + void setUp() { + testUser = User.builder() + .id(1L) + .email("test@example.com") + .fullName("Test User") + .build(); + + testOrder = Order.builder() + .id(1L) + .orderCode(1001L) + .user(testUser) + .build(); + + testPayment = Payment.builder() + .id(1L) + .amount(BigDecimal.valueOf(100.00)) + .method(PaymentMethod.BANK_TRANSFER) + .order(testOrder) + .build(); + + testTransaction = Transaction.builder() + .id(1L) + .user(testUser) + .type(TransactionType.PAYMENT) + .amount(BigDecimal.valueOf(-100.00)) + .paymentMethod(PaymentMethod.BANK_TRANSFER) + .status(TransactionStatus.PENDING) + .order(testOrder) + .payment(testPayment) + .transactionCode("TXN123456789") + .description("Payment for order #1001") + .build(); + } + + @Test + void createPaymentTransaction_Success() { + // Given + when(transactionRepository.save(any(Transaction.class))).thenAnswer(invocation -> { + Transaction saved = invocation.getArgument(0); + saved.setId(1L); + return saved; + }); + + // When + Transaction result = transactionService.createPaymentTransaction(testPayment, testUser); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getType()).isEqualTo(TransactionType.PAYMENT); + assertThat(result.getAmount()).isNegative(); // Payment is negative + assertThat(result.getStatus()).isEqualTo(TransactionStatus.PENDING); + assertThat(result.getPaymentMethod()).isEqualTo(PaymentMethod.BANK_TRANSFER); + assertThat(result.getTransactionCode()).isNotNull(); + verify(transactionRepository).save(any(Transaction.class)); + } + + @Test + void createRefundTransaction_Success() { + // Given + when(paymentRepository.findByOrder(testOrder)).thenReturn(Optional.of(testPayment)); + when(transactionRepository.save(any(Transaction.class))).thenAnswer(invocation -> { + Transaction saved = invocation.getArgument(0); + saved.setId(2L); + return saved; + }); + + // When + Transaction result = transactionService.createRefundTransaction( + testOrder, + BigDecimal.valueOf(50.00), + "Order cancelled" + ); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getType()).isEqualTo(TransactionType.REFUND); + assertThat(result.getAmount()).isEqualByComparingTo(BigDecimal.valueOf(50.00)); + assertThat(result.getStatus()).isEqualTo(TransactionStatus.SUCCESS); + assertThat(result.getCompletedAt()).isNotNull(); + assertThat(result.getDescription()).contains("Hoàn tiền").contains("Order cancelled"); + verify(transactionRepository).save(any(Transaction.class)); + } + + @Test + void createRefundTransaction_NoPayment_Success() { + // Given + when(paymentRepository.findByOrder(testOrder)).thenReturn(Optional.empty()); + when(transactionRepository.save(any(Transaction.class))).thenAnswer(invocation -> { + Transaction saved = invocation.getArgument(0); + saved.setId(2L); + return saved; + }); + + // When + Transaction result = transactionService.createRefundTransaction( + testOrder, + BigDecimal.valueOf(50.00), + "Order cancelled" + ); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getPaymentMethod()).isNull(); + assertThat(result.getPayment()).isNull(); + verify(transactionRepository).save(any(Transaction.class)); + } + + @Test + void updateTransactionStatus_ToSuccess_Success() { + // Given + when(transactionRepository.findById(1L)).thenReturn(Optional.of(testTransaction)); + when(transactionRepository.save(any(Transaction.class))).thenAnswer(invocation -> invocation.getArgument(0)); + + // When + Transaction result = transactionService.updateTransactionStatus(1L, TransactionStatus.SUCCESS); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getStatus()).isEqualTo(TransactionStatus.SUCCESS); + assertThat(result.getCompletedAt()).isNotNull(); + verify(transactionRepository).save(testTransaction); + } + + @Test + void updateTransactionStatus_ToFailed_Success() { + // Given + when(transactionRepository.findById(1L)).thenReturn(Optional.of(testTransaction)); + when(transactionRepository.save(any(Transaction.class))).thenAnswer(invocation -> invocation.getArgument(0)); + + // When + Transaction result = transactionService.updateTransactionStatus(1L, TransactionStatus.FAILED); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getStatus()).isEqualTo(TransactionStatus.FAILED); + assertThat(result.getCompletedAt()).isNull(); // Only set for SUCCESS + verify(transactionRepository).save(testTransaction); + } + + @Test + void updateTransactionStatus_TransactionNotFound_ThrowsException() { + // Given + when(transactionRepository.findById(999L)).thenReturn(Optional.empty()); + + // When & Then + assertThatThrownBy(() -> transactionService.updateTransactionStatus(999L, TransactionStatus.SUCCESS)) + .isInstanceOf(AppException.class) + .hasMessageContaining("Transaction không tồn tại"); + verify(transactionRepository, never()).save(any()); + } + + @Test + void updateTransactionOnPaymentSuccess_Success() { + // Given + when(transactionRepository.findByPayment(testPayment)).thenReturn(Optional.of(testTransaction)); + when(transactionRepository.save(any(Transaction.class))).thenAnswer(invocation -> invocation.getArgument(0)); + + // When + Transaction result = transactionService.updateTransactionOnPaymentSuccess(testPayment, "EXT123456"); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getStatus()).isEqualTo(TransactionStatus.SUCCESS); + assertThat(result.getExternalTransactionId()).isEqualTo("EXT123456"); + assertThat(result.getCompletedAt()).isNotNull(); + verify(transactionRepository).save(testTransaction); + } + + @Test + void updateTransactionOnPaymentSuccess_TransactionNotFound_ThrowsException() { + // Given + when(transactionRepository.findByPayment(testPayment)).thenReturn(Optional.empty()); + + // When & Then + assertThatThrownBy(() -> transactionService.updateTransactionOnPaymentSuccess(testPayment, "EXT123456")) + .isInstanceOf(AppException.class) + .hasMessageContaining("Transaction không tồn tại"); + } + + @Test + void getUserTransactions_WithType_Success() { + // Given + Pageable pageable = PageRequest.of(0, 10); + Page transactionsPage = new PageImpl<>(Arrays.asList(testTransaction)); + + when(userRepository.findById(1L)).thenReturn(Optional.of(testUser)); + when(transactionRepository.findByUserAndTypeOrderByCreatedAtDesc(testUser, TransactionType.PAYMENT, pageable)) + .thenReturn(transactionsPage); + + // When + Page result = transactionService.getUserTransactions(1L, TransactionType.PAYMENT, pageable); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getContent()).hasSize(1); + assertThat(result.getContent().get(0).getType()).isEqualTo(TransactionType.PAYMENT); + verify(transactionRepository).findByUserAndTypeOrderByCreatedAtDesc(testUser, TransactionType.PAYMENT, pageable); + } + + @Test + void getUserTransactions_WithoutType_Success() { + // Given + Pageable pageable = PageRequest.of(0, 10); + Page transactionsPage = new PageImpl<>(Arrays.asList(testTransaction)); + + when(userRepository.findById(1L)).thenReturn(Optional.of(testUser)); + when(transactionRepository.findByUserOrderByCreatedAtDesc(testUser, pageable)) + .thenReturn(transactionsPage); + + // When + Page result = transactionService.getUserTransactions(1L, null, pageable); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getContent()).hasSize(1); + verify(transactionRepository).findByUserOrderByCreatedAtDesc(testUser, pageable); + } + + @Test + void getUserTransactions_UserNotFound_ThrowsException() { + // Given + when(userRepository.findById(999L)).thenReturn(Optional.empty()); + + // When & Then + assertThatThrownBy(() -> transactionService.getUserTransactions(999L, null, PageRequest.of(0, 10))) + .isInstanceOf(AppException.class) + .hasFieldOrPropertyWithValue("errorCode", ErrorCode.USER_NOT_FOUND); + } + + @Test + void getAllTransactions_WithFilters_Success() { + // Given + Pageable pageable = PageRequest.of(0, 20); + LocalDateTime startDate = LocalDateTime.now().minusDays(7); + LocalDateTime endDate = LocalDateTime.now(); + Page transactionsPage = new PageImpl<>(Arrays.asList(testTransaction)); + + when(transactionRepository.findAllWithFilters( + eq(TransactionType.PAYMENT), + eq(TransactionStatus.SUCCESS), + eq(startDate), + any(LocalDateTime.class), // endDate + 1 day + eq(pageable) + )).thenReturn(transactionsPage); + + // When + Page result = transactionService.getAllTransactions( + TransactionType.PAYMENT, + TransactionStatus.SUCCESS, + startDate, + endDate, + pageable + ); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getContent()).hasSize(1); + } + + @Test + void getTransactionSummary_AllTypes_Success() { + // Given + when(transactionRepository.count()).thenReturn(100L); + when(transactionRepository.countByStatus(TransactionStatus.PENDING)).thenReturn(10L); + when(transactionRepository.sumAmountByFilters(TransactionType.REFUND, TransactionStatus.SUCCESS)) + .thenReturn(BigDecimal.valueOf(500.00)); + when(transactionRepository.sumAmountByFilters(TransactionType.DEPOSIT, TransactionStatus.SUCCESS)) + .thenReturn(BigDecimal.valueOf(1000.00)); + when(transactionRepository.sumAmountByFilters(TransactionType.PAYMENT, TransactionStatus.SUCCESS)) + .thenReturn(BigDecimal.valueOf(-800.00)); + when(transactionRepository.sumAmountByFilters(TransactionType.WITHDRAWAL, TransactionStatus.SUCCESS)) + .thenReturn(BigDecimal.valueOf(-200.00)); + + // When + TransactionSummaryResponse result = transactionService.getTransactionSummary(null, null, null, null); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getTotalTransactions()).isEqualTo(100L); + assertThat(result.getPendingTransactions()).isEqualTo(10L); + assertThat(result.getTotalRevenue()).isEqualByComparingTo(BigDecimal.valueOf(1500.00)); // 500 + 1000 + assertThat(result.getTotalExpense()).isEqualByComparingTo(BigDecimal.valueOf(1000.00)); // |−800| + |−200| + assertThat(result.getNetAmount()).isEqualByComparingTo(BigDecimal.valueOf(500.00)); // 1500 - 1000 + } + + @Test + void getTransactionSummary_OnlyPayments_Success() { + // Given + when(transactionRepository.count()).thenReturn(50L); + when(transactionRepository.countByStatus(TransactionStatus.PENDING)).thenReturn(5L); + when(transactionRepository.sumAmountByFilters(TransactionType.PAYMENT, TransactionStatus.SUCCESS)) + .thenReturn(BigDecimal.valueOf(-800.00)); + + // When + TransactionSummaryResponse result = transactionService.getTransactionSummary( + TransactionType.PAYMENT, null, null, null + ); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getTotalRevenue()).isEqualByComparingTo(BigDecimal.ZERO); + assertThat(result.getTotalExpense()).isEqualByComparingTo(BigDecimal.valueOf(800.00)); + assertThat(result.getNetAmount()).isEqualByComparingTo(BigDecimal.valueOf(-800.00)); + } + + @Test + void getTransactionDetail_Success() { + // Given + when(transactionRepository.findById(1L)).thenReturn(Optional.of(testTransaction)); + + // When + TransactionResponse result = transactionService.getTransactionDetail(1L); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getId()).isEqualTo(1L); + assertThat(result.getTransactionCode()).isEqualTo("TXN123456789"); + assertThat(result.getType()).isEqualTo(TransactionType.PAYMENT); + assertThat(result.getUserName()).isEqualTo("Test User"); + assertThat(result.getUserEmail()).isEqualTo("test@example.com"); + } + + @Test + void getTransactionDetail_NotFound_ThrowsException() { + // Given + when(transactionRepository.findById(999L)).thenReturn(Optional.empty()); + + // When & Then + assertThatThrownBy(() -> transactionService.getTransactionDetail(999L)) + .isInstanceOf(AppException.class) + .hasMessageContaining("Transaction không tồn tại"); + } + + @Test + void generateTransactionCode_ReturnsUniqueCode() { + // When + String code1 = transactionService.generateTransactionCode(); + String code2 = transactionService.generateTransactionCode(); + + // Then + assertThat(code1).startsWith("TXN"); + assertThat(code2).startsWith("TXN"); + assertThat(code1).hasSize(15); // TXN + 12 digits + // Codes should be different (with high probability) + } +} \ No newline at end of file