diff --git a/src/main/java/de/zendric/app/xpensely_server/model/Exception/ResourceNotFoundException.java b/src/main/java/de/zendric/app/xpensely_server/model/Exception/ResourceNotFoundException.java new file mode 100644 index 0000000..46e0582 --- /dev/null +++ b/src/main/java/de/zendric/app/xpensely_server/model/Exception/ResourceNotFoundException.java @@ -0,0 +1,11 @@ +package de.zendric.app.xpensely_server.model.Exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(HttpStatus.NOT_FOUND) +public class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/de/zendric/app/xpensely_server/repo/ExpenseListRepository.java b/src/main/java/de/zendric/app/xpensely_server/repo/ExpenseListRepository.java index c42b820..5a81911 100644 --- a/src/main/java/de/zendric/app/xpensely_server/repo/ExpenseListRepository.java +++ b/src/main/java/de/zendric/app/xpensely_server/repo/ExpenseListRepository.java @@ -3,13 +3,24 @@ package de.zendric.app.xpensely_server.repo; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import de.zendric.app.xpensely_server.model.ExpenseList; @Repository public interface ExpenseListRepository extends JpaRepository { + List findByOwnerId(Long ownerId); ExpenseList findByInviteCode(String inviteCode); -} \ No newline at end of file + + @Query("SELECT el FROM ExpenseList el WHERE el.owner.id = :userId OR el.sharedWith.id = :sharedUserId") + List findByOwnerIdOrSharedWithId(@Param("userId") Long userId, + @Param("sharedUserId") Long sharedUserId); + + @Query("SELECT el FROM ExpenseList el WHERE el.owner.username = :username OR el.sharedWith.username = :sharedUsername") + List findByOwnerUsernameOrSharedWithUsername(@Param("username") String username, + @Param("sharedUsername") String sharedUsername); +} diff --git a/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java b/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java index 0ee1be6..3ac2301 100644 --- a/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java +++ b/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java @@ -7,7 +7,6 @@ import java.util.List; import java.util.Optional; import java.util.UUID; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -18,20 +17,15 @@ import de.zendric.app.xpensely_server.model.XpenselyCustomCategory; import de.zendric.app.xpensely_server.repo.ExpenseListRepository; import de.zendric.app.xpensely_server.repo.ExpenseRepository; import de.zendric.app.xpensely_server.repo.XpenselyCustomCategoryRepository; -import jakarta.persistence.EntityManager; @Service @Transactional public class ExpenseListService { - private ExpenseListRepository repository; + private final ExpenseListRepository repository; private final ExpenseRepository expenseRepository; - private XpenselyCustomCategoryRepository customCategoryRepository; + private final XpenselyCustomCategoryRepository customCategoryRepository; - @Autowired - private EntityManager entityManager; - - @Autowired public ExpenseListService(ExpenseListRepository repository, ExpenseRepository expenseRepository, XpenselyCustomCategoryRepository customCategoryRepository) { this.repository = repository; @@ -68,38 +62,11 @@ public class ExpenseListService { } public List findByUserId(Long id) { - List allLists = repository.findAll(); - List userSpecificList = new ArrayList<>(); - for (ExpenseList expenseList : allLists) { - AppUser sharedWith = expenseList.getSharedWith(); - - if (expenseList.getOwner().getId().equals(id)) { - userSpecificList.add(expenseList); - } else { - if (sharedWith != null && sharedWith.getId().equals(id)) { - userSpecificList.add(expenseList); - } - } - } - return userSpecificList; + return repository.findByOwnerIdOrSharedWithId(id, id); } public List findByUsername(String username) { - List allLists = repository.findAll(); - List userSpecificList = new ArrayList<>(); - for (ExpenseList expenseList : allLists) { - AppUser sharedWith = expenseList.getSharedWith(); - - if (expenseList.getOwner().getUsername().equals(username)) { - userSpecificList.add(expenseList); - } else { - if (sharedWith != null && sharedWith.getUsername().equals(username)) { - userSpecificList.add(expenseList); - } - } - } - return userSpecificList; - + return repository.findByOwnerUsernameOrSharedWithUsername(username, username); } public Expense addExpenseToList(Long expenseListId, Expense expense) { diff --git a/src/main/java/de/zendric/app/xpensely_server/services/UserService.java b/src/main/java/de/zendric/app/xpensely_server/services/UserService.java index dc0dee7..a86d665 100644 --- a/src/main/java/de/zendric/app/xpensely_server/services/UserService.java +++ b/src/main/java/de/zendric/app/xpensely_server/services/UserService.java @@ -1,11 +1,11 @@ package de.zendric.app.xpensely_server.services; import java.util.List; -import java.util.Optional; import org.springframework.stereotype.Service; import de.zendric.app.xpensely_server.model.AppUser; +import de.zendric.app.xpensely_server.model.Exception.ResourceNotFoundException; import de.zendric.app.xpensely_server.model.Exception.UsernameAlreadyExistsException; import de.zendric.app.xpensely_server.repo.UserRepository; @@ -29,36 +29,24 @@ public class UserService { } public AppUser getUser(Long id) { - Optional user = userRepository.findById(id); - if (user.isPresent()) { - return user.get(); - } else - return null; + return userRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + id)); } public AppUser deleteUserById(Long id) { - Optional user = userRepository.findById(id); - if (user.isPresent()) { - userRepository.deleteById(id); - return user.get(); - } else - return null; + AppUser user = userRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + id)); + userRepository.deleteById(id); + return user; } public AppUser getUserByName(String username) { - Optional optUser = userRepository.findByUsername(username); - if (optUser.isPresent()) { - return optUser.get(); - } else - return null; + return userRepository.findByUsername(username) + .orElseThrow(() -> new ResourceNotFoundException("User not found: " + username)); } public AppUser getUserByGoogleId(String id) { - Optional optUser = userRepository.findByGoogleId(id); - if (optUser.isPresent()) { - return optUser.get(); - } else - return null; + return userRepository.findByGoogleId(id) + .orElseThrow(() -> new ResourceNotFoundException("User not found with Google ID: " + id)); } - -} \ No newline at end of file +} diff --git a/src/test/java/de/zendric/app/xpensely_Server/services/ExpenseListServiceTest.java b/src/test/java/de/zendric/app/xpensely_Server/services/ExpenseListServiceTest.java new file mode 100644 index 0000000..ae1fc98 --- /dev/null +++ b/src/test/java/de/zendric/app/xpensely_Server/services/ExpenseListServiceTest.java @@ -0,0 +1,57 @@ +package de.zendric.app.xpensely_Server.services; + +import de.zendric.app.xpensely_server.model.AppUser; +import de.zendric.app.xpensely_server.model.ExpenseList; +import de.zendric.app.xpensely_server.repo.ExpenseListRepository; +import de.zendric.app.xpensely_server.repo.ExpenseRepository; +import de.zendric.app.xpensely_server.repo.XpenselyCustomCategoryRepository; +import de.zendric.app.xpensely_server.services.ExpenseListService; +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.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.never; + +@ExtendWith(MockitoExtension.class) +class ExpenseListServiceTest { + + @Mock ExpenseListRepository repository; + @Mock ExpenseRepository expenseRepository; + @Mock XpenselyCustomCategoryRepository customCategoryRepository; + + @InjectMocks + ExpenseListService service; + + @Test + void findByUserId_usesRepositoryQuery_notFindAll() { + AppUser owner = new AppUser(); owner.setId(1L); + ExpenseList list = new ExpenseList(); list.setId(10L); list.setOwner(owner); + when(repository.findByOwnerIdOrSharedWithId(1L, 1L)).thenReturn(List.of(list)); + + List result = service.findByUserId(1L); + + assertThat(result).hasSize(1); + verify(repository).findByOwnerIdOrSharedWithId(1L, 1L); + verify(repository, never()).findAll(); + } + + @Test + void findByUsername_usesRepositoryQuery_notFindAll() { + AppUser owner = new AppUser(); owner.setId(1L); owner.setUsername("alice"); + ExpenseList list = new ExpenseList(); list.setId(10L); list.setOwner(owner); + when(repository.findByOwnerUsernameOrSharedWithUsername("alice", "alice")).thenReturn(List.of(list)); + + List result = service.findByUsername("alice"); + + assertThat(result).hasSize(1); + verify(repository).findByOwnerUsernameOrSharedWithUsername("alice", "alice"); + verify(repository, never()).findAll(); + } +} diff --git a/src/test/java/de/zendric/app/xpensely_Server/services/UserServiceTest.java b/src/test/java/de/zendric/app/xpensely_Server/services/UserServiceTest.java new file mode 100644 index 0000000..9202c92 --- /dev/null +++ b/src/test/java/de/zendric/app/xpensely_Server/services/UserServiceTest.java @@ -0,0 +1,75 @@ +package de.zendric.app.xpensely_Server.services; + +import de.zendric.app.xpensely_server.model.AppUser; +import de.zendric.app.xpensely_server.model.Exception.ResourceNotFoundException; +import de.zendric.app.xpensely_server.repo.UserRepository; +import de.zendric.app.xpensely_server.services.UserService; +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.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class UserServiceTest { + + @Mock + UserRepository userRepository; + + @InjectMocks + UserService userService; + + @Test + void getUserByName_throwsResourceNotFound_whenUserMissing() { + when(userRepository.findByUsername("ghost")).thenReturn(Optional.empty()); + + assertThatThrownBy(() -> userService.getUserByName("ghost")) + .isInstanceOf(ResourceNotFoundException.class) + .hasMessageContaining("ghost"); + } + + @Test + void getUserByName_returnsUser_whenFound() { + AppUser user = new AppUser(); + user.setId(1L); + user.setUsername("alice"); + when(userRepository.findByUsername("alice")).thenReturn(Optional.of(user)); + + AppUser result = userService.getUserByName("alice"); + + assertThat(result.getUsername()).isEqualTo("alice"); + } + + @Test + void getUser_throwsResourceNotFound_whenIdMissing() { + when(userRepository.findById(99L)).thenReturn(Optional.empty()); + + assertThatThrownBy(() -> userService.getUser(99L)) + .isInstanceOf(ResourceNotFoundException.class) + .hasMessageContaining("99"); + } + + @Test + void deleteUserById_throwsResourceNotFound_whenIdMissing() { + when(userRepository.findById(99L)).thenReturn(Optional.empty()); + + assertThatThrownBy(() -> userService.deleteUserById(99L)) + .isInstanceOf(ResourceNotFoundException.class) + .hasMessageContaining("99"); + } + + @Test + void getUserByGoogleId_throwsResourceNotFound_whenMissing() { + when(userRepository.findByGoogleId("gid-404")).thenReturn(Optional.empty()); + + assertThatThrownBy(() -> userService.getUserByGoogleId("gid-404")) + .isInstanceOf(ResourceNotFoundException.class) + .hasMessageContaining("gid-404"); + } +}