diff --git a/src/main/java/org/example/EMFactory.java b/src/main/java/org/example/EMFactory.java new file mode 100644 index 00000000..7d742779 --- /dev/null +++ b/src/main/java/org/example/EMFactory.java @@ -0,0 +1,30 @@ +package org.example; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.Persistence; + +public final class EMFactory { + private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("library_system"); + + private EMFactory() { + // Prevent instantiation + } + + public static void init(){ + // Call this once to initialize the factory at app start + } + + // Use this in try-with + public static EntityManager getEntityManager() { + return emf.createEntityManager(); + } + + + // Close the factory + public static void close() { + if (emf.isOpen()) { + emf.close(); + } + } +} diff --git a/src/main/java/org/example/User.java b/src/main/java/org/example/User.java index 73cd8888..aded1ba1 100644 --- a/src/main/java/org/example/User.java +++ b/src/main/java/org/example/User.java @@ -62,6 +62,14 @@ public void setFirstName(String firstName) { this.firstName = firstName; } + public String getFullName() { + return firstName + " " + lastName; + } + + public String getDisplayName() { + return getFullName() + " (" + username + ")"; + } + public Long getUserId() { return userId; } diff --git a/src/main/java/org/example/user/SessionManager.java b/src/main/java/org/example/user/SessionManager.java new file mode 100644 index 00000000..97b06eef --- /dev/null +++ b/src/main/java/org/example/user/SessionManager.java @@ -0,0 +1,33 @@ +package org.example.user; + +import org.example.User; + +public class SessionManager { + private static User currentUser = null; + + // Log in user by setting currentUser to the passed in user + public static void login(User user) { + currentUser = user; + } + + // Log out user by setting currentUser to null + public static void logout() { + currentUser = null; + } + + public static User getCurrentUser() { + return currentUser; + } + + public static String loggedInDisplayName() { + if (currentUser != null) + return currentUser.getDisplayName(); + else + return "Gäst"; + } + + // Returns true if a user is logged in + public static boolean isLoggedIn() { + return currentUser != null; + } +} diff --git a/src/main/java/org/example/user/UserCLI.java b/src/main/java/org/example/user/UserCLI.java new file mode 100644 index 00000000..791e6811 --- /dev/null +++ b/src/main/java/org/example/user/UserCLI.java @@ -0,0 +1,222 @@ +package org.example.user; + +import org.example.User; +import java.util.Scanner; + +public class UserCLI { + private final UserService userService; + private final Scanner scanner; + + // Constructor + public UserCLI(UserService userService, Scanner scanner) { + this.userService = userService; + this.scanner = scanner; + } + + public void userMenu() { + // Make sure user is not already logged in + if (SessionManager.isLoggedIn()) return; + + boolean closeMenu = false; + while (!closeMenu && !SessionManager.isLoggedIn()) { + System.out.println(appName()); + System.out.println("Logga in/Skapa användare Inloggad som: " + SessionManager.loggedInDisplayName()); + System.out.println("========================================================"); + System.out.println("1. Logga in | 2. Skapa användare | 3. Tillbaka"); + System.out.print("Menyval: "); + String choice = scanner.nextLine(); + switch (choice) { + case "1" -> loginMenu(); + case "2" -> createUserMenu(); + case "3" -> closeMenu = true; + default -> System.out.println("Ogiltigt val."); + } + + // Check if user is logged in, if so, close menu + if (SessionManager.isLoggedIn()) { + closeMenu = true; + } + } + } + + public void manageUserMenu() { + // Make sure user is logged in + if (!SessionManager.isLoggedIn()) return; + + boolean closeMenu = false; + while (!closeMenu && SessionManager.isLoggedIn()) { + System.out.println(appName()); + System.out.println("Hantera användare Inloggad som: " + SessionManager.loggedInDisplayName()); + System.out.println("========================================================"); + System.out.println("1. Uppdatera användare | 2. Radera användare | 3. Logga ut | 4. Tillbaka"); + System.out.print("Menyval: "); + String choice = scanner.nextLine(); + switch (choice) { + case "1" -> updateUserMenu(); + case "2" -> deleteUser(); + case "3" -> logout(); + case "4" -> closeMenu = true; + default -> System.out.println("Ogiltigt val."); + } + } + } + + public void loginMenu() { + // Make sure user is not already logged in + if (SessionManager.isLoggedIn()) return; + + while (!SessionManager.isLoggedIn()) { + try { + System.out.println(appName()); + System.out.println("Inloggningsuppgifter Inloggad som: " + SessionManager.loggedInDisplayName()); + System.out.println("========================================================"); + System.out.println("(Skriv 'avbryt' för att avbryta inloggningen)"); + // Ask for credentials, prompt to enable exiting from menu + String username = prompt("Användarnamn: "); + String password = prompt("Lösenord: "); + + // Check credentials with database + User user = userService.login(username, password); + + // Tell SessionManager to remember user + SessionManager.login(user); + + if (SessionManager.isLoggedIn()){ + System.out.println("Inloggningen lyckades. Välkommen " + user.getFirstName() + "!"); + } + else { + System.out.println("Inloggningen misslyckades."); + } + } + // Catch validation errors + catch (IllegalArgumentException e) { + System.out.println("Felmeddelande: " + e.getMessage()); + } + // Catch our "exit" exception" + catch (RuntimeException e){ + if (e.getMessage().equals("AVBRUTET")) { + return; + } + // Catch any other exceptions + throw e; + } + } + } + + public void logout() { + SessionManager.logout(); + System.out.println("Du har loggats ut."); + } + + public void createUserMenu() { + // Make sure user is not already logged in + if (SessionManager.isLoggedIn()) return; + + boolean success = false; + + while (!success) { + try { + System.out.println(appName()); + System.out.println("Skapa användare Inloggad som: " + SessionManager.loggedInDisplayName()); + System.out.println("========================================================"); + System.out.println("(Skriv 'avbryt' för att avbryta skapandet av ny användare)"); + + String firstName = prompt("Förnamn: "); + String lastName = prompt("Efternamn: "); + String email = prompt("Email: "); + String password = prompt("Lösenord: "); + + // Create user + User user = userService.createUser(firstName, lastName, email, password); + + System.out.println("Välkommen " + user.getFirstName() + "!"); + System.out.println("Ditt användarnamn är: " + user.getUsername()); + success = true; + } + // Catch validation errors + catch (IllegalArgumentException e) { + System.out.println("Felmeddelande: " + e.getMessage()); + } + // Catch our "exit" exception" + catch (RuntimeException e){ + if (e.getMessage().equals("AVBRUTET")) { + return; + } + // Catch any other exceptions + throw e; + } + } + } + + public void updateUserMenu() { + // Make sure user is logged in + if (!SessionManager.isLoggedIn()) return; + + User currentUser = SessionManager.getCurrentUser(); + + try { + System.out.println(appName()); + System.out.println("Uppdatera användare Inloggad som: " + SessionManager.loggedInDisplayName()); + System.out.println("========================================================"); + System.out.println("(Skriv 'avbryt' för att avbryta uppdateringen av användaren)"); + + String firstName = prompt("Nytt förnamn: "); + String lastName = prompt("Nytt efternamn: "); + String email = prompt("Ny email: "); + String password = prompt("Nytt lösenord: "); + + // If user didn't enter anything, keep current value + if (firstName.isBlank()) firstName = currentUser.getFirstName(); + if (lastName.isBlank()) lastName = currentUser.getLastName(); + if (email.isBlank()) email = currentUser.getEmail(); + if (password.isBlank()) password = currentUser.getPassword(); + + User updatedUser = userService.updateUser(currentUser.getUserId(), firstName, lastName, email, password); + SessionManager.login(updatedUser); // Update session with new user + System.out.println("Användaren har uppdaterats!"); + } + // Catch validation errors + catch (IllegalArgumentException e) { + System.out.println("Kunde inte uppdatera: " + e.getMessage()); + } + // Catch our "exit" exception" + catch (RuntimeException e){ + if (e.getMessage().equals("AVBRUTET")) { + return; + } + // Catch any other exceptions + throw e; + } + } + + public void deleteUser() { + // Make sure user is logged in + if (!SessionManager.isLoggedIn()) return; + + System.out.print("Är du säker på att du vill radera din användare? (ja/nej): "); + if (scanner.nextLine().equalsIgnoreCase("ja")) { + userService.deleteUserById(SessionManager.getCurrentUser().getUserId()); + SessionManager.logout(); + System.out.println("Användaren har raderats och du har loggats ut."); + } else { + System.out.println("Du har valt att behålla användaren."); + } + } + + // Method to enable user to exit menu + private String prompt(String message) { + // Print the passed argument + System.out.print(message); + String input = scanner.nextLine(); + // Exit from current menu if "avbryt" is entered + if (input.equalsIgnoreCase("avbryt")) { + throw new RuntimeException("AVBRUTET"); + } + return input; + } + + private String appName() { + return "\n\nBIBLIOTEKSSYSTEMET"; + } + +} diff --git a/src/main/java/org/example/user/UserService.java b/src/main/java/org/example/user/UserService.java new file mode 100644 index 00000000..56de7abd --- /dev/null +++ b/src/main/java/org/example/user/UserService.java @@ -0,0 +1,141 @@ +package org.example.user; + +import org.example.User; +import org.example.user.repository.UserRepository; + +import java.util.List; +import java.util.regex.Pattern; + +// User business logic +public class UserService { + + private static final Pattern EMAIL_PATTERN = Pattern.compile("^[a-zA-Z0-9_!#$%&’*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$"); + + private final UserRepository userRepository; + + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + public User login(String username, String password) { + User user = userRepository.findByUsername(username); + + // Check if a user was found + if (user == null) { + throw new IllegalArgumentException("Kunde inte hitta användarnamn \"" + username + "\""); + } + + // Check if the password is correct + if (!user.getPassword().equals(password) ) { + throw new IllegalArgumentException("Ogiltigt lösenord"); + } + + return user; + } + + public User createUser(String firstName, String lastName, String email, String password) { + + validate(firstName, lastName, email, password); + + // Generate username + String username = generateUsername(firstName, lastName); + + User newUser = new User(); + newUser.setFirstName(firstName); + newUser.setLastName(lastName); + newUser.setEmail(email); + newUser.setPassword(password); + newUser.setUsername(username); + return userRepository.createUser(newUser); + } + + public User updateUser(Long id, String firstName, String lastName, String email, String password) { + // Get user by id + User user = userRepository.findById(id); + + // Check if a user was found + if (user == null) { + throw new IllegalArgumentException("Kunde inte hitta användare med id: " + id); + } + + validate(firstName, lastName, email, password); + + // Update user + user.setFirstName(firstName); + user.setLastName(lastName); + user.setEmail(email); + user.setPassword(password); + return userRepository.updateUser(user); + + } + + public boolean deleteUserById(Long id) { + return userRepository.deleteUserById(id); + } + + public boolean deleteUserByUsername(String username) { + return userRepository.deleteUserByUsername(username); + } + + public User findById(Long id) { + return userRepository.findById(id); + } + + public User findByEmail(String email) { + return userRepository.findByEmail(email); + } + + public User findByUsername(String username) { + return userRepository.findByUsername(username); + } + + public List findAllUsers() { + return userRepository.findAllUsers(); + } + + private String generateUsername(String firstName, String lastName) { + // Create username from first and last name (Dennis Dennisson = denden) + String concatUsername = (createSubstrings(firstName) + createSubstrings(lastName)).toLowerCase(); + String username = concatUsername; + + int counter = 1; + while(userRepository.findByUsername(username) != null) { + username = concatUsername + counter; + counter++; + } + return username; + } + + private String createSubstrings(String name) { + // Length of 3 or length of name if shorter than 3 + int length = Math.min(name.length(), 3); + return name.substring(0, length); + } + + private void validate (String firstName, String lastName, String email, String password) { + // Check for null or empty + if (isInvalid(firstName) || isInvalid(lastName) || isInvalid(email) || isInvalid(password)) { + throw new IllegalArgumentException("Inga fält får lämnas tomma."); + } + + // Validate email with regex pattern + if (!EMAIL_PATTERN.matcher(email).matches()) { + throw new IllegalArgumentException("Ogiltigt emailformat."); + } + + // Validate password length + if (password.length() < 3) { + throw new IllegalArgumentException("Lösenordet måste vara minst 3 karaktärer långt."); + } + + // Check for existing email + if (userRepository.findByEmail(email) != null) { + throw new IllegalArgumentException("En användare med denna emailadress finns redan."); + } + } + + private boolean isInvalid(String value) { + return value == null || value.isBlank(); + } + +} diff --git a/src/main/java/org/example/user/repository/JpaUserRepository.java b/src/main/java/org/example/user/repository/JpaUserRepository.java new file mode 100644 index 00000000..b7742e8e --- /dev/null +++ b/src/main/java/org/example/user/repository/JpaUserRepository.java @@ -0,0 +1,91 @@ +package org.example.user.repository; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.NoResultException; +import org.example.EMFactory; +import org.example.User; + +import java.util.List; + +public class JpaUserRepository implements UserRepository { + + @Override + public User createUser(User user) { + try (EntityManager em = EMFactory.getEntityManager()) { + em.getTransaction().begin(); + em.persist(user); + em.getTransaction().commit(); + return user; + } + } + + @Override + public User updateUser(User user) { + try (EntityManager em = EMFactory.getEntityManager()) { + em.getTransaction().begin(); + User updatedUser = em.merge(user); + em.getTransaction().commit(); + return updatedUser; + } + } + + @Override + public boolean deleteUserById(Long id) { + try (EntityManager em = EMFactory.getEntityManager()) { + em.getTransaction().begin(); + User user = em.find(User.class, id); + if (user != null) { + em.remove(user); + em.getTransaction().commit(); + return true; + } + em.getTransaction().rollback(); + return false; + } + } + + @Override + public boolean deleteUserByUsername(String username) { + User user = findByUsername(username); + if (user != null) { + return deleteUserById(user.getUserId()); + } + return false; + } + + @Override + public User findById(Long id) { + try (EntityManager em = EMFactory.getEntityManager()) { + return em.find(User.class, id); + } + } + + @Override + public User findByEmail(String email) { + try (EntityManager em = EMFactory.getEntityManager()) { + return em.createQuery("SELECT u FROM User u WHERE u.email = :email", User.class) + .setParameter("email", email) + .getSingleResult(); + } catch (NoResultException e) { + return null; + } + } + + @Override + public User findByUsername(String username) { + try (EntityManager em = EMFactory.getEntityManager()) { + return em.createQuery("SELECT u FROM User u WHERE u.username = :username", User.class) + .setParameter("username", username) + .getSingleResult(); + } catch (NoResultException e) { + return null; + } + } + + @Override + public List findAllUsers() { + try (EntityManager em = EMFactory.getEntityManager()) { + return em.createQuery("SELECT u FROM User u", User.class).getResultList(); + } + } +} diff --git a/src/main/java/org/example/user/repository/UserRepository.java b/src/main/java/org/example/user/repository/UserRepository.java new file mode 100644 index 00000000..83a625c8 --- /dev/null +++ b/src/main/java/org/example/user/repository/UserRepository.java @@ -0,0 +1,17 @@ +package org.example.user.repository; + +import org.example.User; +import java.util.List; + +public interface UserRepository { + User createUser(User user); + User updateUser(User user); + boolean deleteUserById(Long id); + boolean deleteUserByUsername(String username); + + User findById(Long id); + User findByEmail(String email); + User findByUsername(String username); + + List findAllUsers(); +} diff --git a/src/main/java/org/example/user/testUserCli.java b/src/main/java/org/example/user/testUserCli.java new file mode 100644 index 00000000..af5ee46b --- /dev/null +++ b/src/main/java/org/example/user/testUserCli.java @@ -0,0 +1,65 @@ +package org.example.user; + +import org.example.EMFactory; +import org.example.user.repository.JpaUserRepository; +import org.example.user.repository.UserRepository; + +import java.util.Scanner; + +public class testUserCli { + private static final Scanner scanner = new Scanner(System.in); + + static void main(String[] args) { + initialize(); // "Warm up" the factory + + final UserRepository userRepository = new JpaUserRepository(); + final UserService userService = new UserService(userRepository); + final UserCLI userCli = new UserCLI(userService, scanner); + + // ####################### + // Application starts here + // ####################### + boolean appRunning = true; + while (appRunning) { + System.out.println("\n\nBIBLIOTEKSSYSTEMET"); + System.out.println("Startsida Inloggad som: " + SessionManager.loggedInDisplayName()); + System.out.println("========================================================"); + + if (SessionManager.isLoggedIn()) { + System.out.println("1. Hantera användare | 2. Logga ut | 0. Avsluta"); + } else { + System.out.println("1. Logga in/Skapa konto | 0. Avsluta"); + } + + System.out.print("Menyval: "); + String choice = scanner.nextLine(); + switch (choice) { + case "1" -> { + if (SessionManager.isLoggedIn()) userCli.manageUserMenu(); + else userCli.userMenu(); + } + case "2" -> { + if (SessionManager.isLoggedIn()) userCli.logout(); + } + case "0" -> appRunning = false; + default -> System.out.println("Ogiltigt val."); + } + } + terminate(); +} + + private static void terminate() { + System.out.println("Bibliotekssystemet avslutat. Välkommen åter!"); + EMFactory.close(); + } + + public static void initialize() { + // Turn off Hibernate logging + System.setProperty("org.jboss.logging.provider", "jdk"); + java.util.logging.Logger hibernateLogger = java.util.logging.Logger.getLogger("org.hibernate"); + hibernateLogger.setLevel(java.util.logging.Level.SEVERE); + + // Initialize EntityManagerFactory + EMFactory.init(); + } +} diff --git a/src/main/resources/META-INF/persistence.xml b/src/main/resources/META-INF/persistence.xml index b6827f13..b0a22b23 100644 --- a/src/main/resources/META-INF/persistence.xml +++ b/src/main/resources/META-INF/persistence.xml @@ -18,10 +18,12 @@ - - - - + + + + + + diff --git a/src/test/java/org/example/UserIT.java b/src/test/java/org/example/UserIT.java new file mode 100644 index 00000000..5e281fd5 --- /dev/null +++ b/src/test/java/org/example/UserIT.java @@ -0,0 +1,128 @@ +package org.example; + +import jakarta.persistence.EntityManager; +import org.example.user.UserService; +import org.example.user.repository.UserRepository; +import org.example.user.repository.JpaUserRepository; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +/* *********************************** */ +/* **** User integration tests **** */ +/* *********************************** */ + +public class UserIT { + + private final UserRepository userRepository = new JpaUserRepository(); + private final UserService userService = new UserService(userRepository); + + // Makes sure we start with a clean slate before/after each test + @BeforeEach + void setUp() { + cleanDatabase(); + } + + @AfterEach + void tearDown() { + cleanDatabase(); + } + + private void cleanDatabase() { + try (EntityManager em = EMFactory.getEntityManager()) { + em.getTransaction().begin(); + em.createQuery("DELETE FROM User").executeUpdate(); + em.getTransaction().commit(); + } + } + + // Test a full user lifecycle to make sure database and business logic work together + @Test + void handlingFullUserLifecycle() { + // Create a new user in the database + User testUser = userService.createUser("Tester", "Testsson", "test@test.com", "assword"); + // Make sure a user was created and returned + assertThat(userService.login(testUser.getUsername(), "assword")) + .isNotNull(); + + // Update user in database + testUser = userService.updateUser(testUser.getUserId(), "Testor", "Nytest", "nytest@nytest.com", "newPassword"); + // Make sure the user was updated in the database + assertThat(userService.findById(testUser.getUserId()).getFirstName()) + .isEqualTo("Testor"); + + // Remove user from database + boolean deleted = userService.deleteUserById(testUser.getUserId()); + // Make sure the user was deleted from the database + assertThat(deleted) + .isTrue(); + assertThat(userService.findById(testUser.getUserId())) + .isNull(); + } + + @Test + void handlingUsernameCollisions() { + // Make sure the database can handle two users with the same username + userService.createUser("Tester", "Testsson", "test@test.com", "assword"); + User user2 = userService.createUser("Testor", "Testarn", "testing@testing.com", "assword"); + + // Make sure the second user got a different username than the first + assertThat(user2.getUsername()).isEqualTo("testes1"); + } + + @Test + void throwsExceptionWhenDuplicateEmails() { + userService.createUser("Tester", "Testsson", "test@test.com", "assword"); + + assertThatThrownBy(() -> + userService.createUser("Testor", "Testarn", "test@test.com", "assword")) + .isInstanceOf(IllegalArgumentException.class); + } + + // Make sure JPQL works correctly when searching for a user by email + @Test + void findUserByEmail() { + userService.createUser("Tester", "Testsson", "test@test.com", "assword"); + + User foundUser = userService.findByEmail("test@test.com"); + + assertThat(foundUser) + .isNotNull(); + assertThat(foundUser.getUsername()) + .isEqualTo("testes"); + } + + // Make sure JPQL works correctly when searching for a user by username + @Test + void findUserByUsername(){ + userService.createUser("Tester", "Testsson", "test@test.com", "assword"); + + User foundUser = userService.findByUsername("testes"); + + assertThat(foundUser) + .isNotNull(); + assertThat(foundUser.getFirstName()) + .isEqualTo("Tester"); + } + + @Test + void receiveFalseWhenDeletingWrongUser() { + boolean result = userService.deleteUserById(1337L); + assertThat(result).isFalse(); + } + + @Test + void shouldFindAllUsers() { + userService.createUser("Tester", "Testsson", "test@test.com", "assword"); + userService.createUser("Testor", "Testarn", "testing@testing.com", "assword"); + + List users = userService.findAllUsers(); + + assertThat(users) + .hasSize(2); + } +} diff --git a/src/test/java/org/example/UserServiceUnitTests.java b/src/test/java/org/example/UserServiceUnitTests.java new file mode 100644 index 00000000..5d82c295 --- /dev/null +++ b/src/test/java/org/example/UserServiceUnitTests.java @@ -0,0 +1,277 @@ +package org.example; + +import org.example.user.UserService; +import org.example.user.repository.UserRepository; + +import org.junit.jupiter.api.*; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.mockito.Mockito.*; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/* *********************************** */ +/* **** UserService Unit Tests **** */ +/* *********************************** */ + +class UserServiceUnitTests { + + @Test + void testLoginFailed_WrongUsername_ShowsErrorMessage() { + // Mock the repository + UserRepository mockRepository = mock(UserRepository.class); + UserService userService = new UserService(mockRepository); + + // Tell the mock to return null when trying to find 'testuser' + when(mockRepository.findByUsername("testuser")).thenReturn(null); + + assertThatThrownBy(() -> userService.login("wronguser", "a$$word")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Kunde inte hitta användarnamn"); + } + + @Test + void testLoginFailed_WrongPassword_ShowsErrorMessage() { + // Mock the repository + UserRepository mockRepository = mock(UserRepository.class); + UserService userService = new UserService(mockRepository); + + // Create a user for testing + User testUser = new User(); + testUser.setUsername("testuser"); + testUser.setPassword("rightpassword"); + + // Tell the mock to return 'testUser' when trying to find 'testuser' + when(mockRepository.findByUsername("testuser")).thenReturn(testUser); + + // Test login with wrong password + assertThatThrownBy(() -> userService.login("testuser", "wrongPassword")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Ogiltigt lösenord"); + } + + @Test + void testLogin_Success() { + // Mock the repository + UserRepository mockRepository = mock(UserRepository.class); + UserService userService = new UserService(mockRepository); + + // Create a user for testing + User testUser = new User(); + testUser.setUsername("testuser"); + testUser.setPassword("a$$word"); + + // Tell the mock to return 'testUser' when trying to find 'testuser' + when(mockRepository.findByUsername("testuser")).thenReturn(testUser); + + User result = userService.login("testuser", "a$$word"); + + assertThat(result).isNotNull(); + assertThat(result.getUsername()).isEqualTo("testuser"); + } + + @Test + void testCreateUser_Success() { + UserRepository mockRepository = mock(UserRepository.class); + UserService userService = new UserService(mockRepository); + + // !Help from AI!: + // When createUser is called, return the user that got sent in + when(mockRepository.createUser(any(User.class))).thenAnswer(invocation -> invocation.getArgument(0)); + + // Tell test there's no username collision + when(mockRepository.findByUsername(anyString())).thenReturn(null); + + // Create a user + User created = userService.createUser("Test", "User", "test@test.com", "a$$word"); + + // Make sure everything went through + assertThat(created.getUsername()).isEqualTo("tesuse"); + assertThat(created.getFirstName()).isEqualTo("Test"); + assertThat(created.getEmail()).isEqualTo("test@test.com"); + + verify(mockRepository, times(1)).createUser(any(User.class)); + } + + @Test + void testCreateUser_InvalidEmail_ThrowsException() { + UserRepository mockRepository = mock(UserRepository.class); + UserService userService = new UserService(mockRepository); + + assertThatThrownBy(() -> + userService.createUser("Test", "User", "invalidemail", "a$$word")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Ogiltigt emailformat."); + } + + @Test + void testCreateUser_ShortPassword_ThrowsException() { + UserRepository mockRepository = mock(UserRepository.class); + UserService userService = new UserService(mockRepository); + + assertThatThrownBy(() -> + userService.createUser("Test", "User", "test@test.com", "12")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Lösenordet måste vara minst 3 karaktärer långt."); + } + + @Test + void testCreateUser_EmptyFields_ThrowsException() { + UserRepository mockRepository = mock(UserRepository.class); + UserService userService = new UserService(mockRepository); + + assertThatThrownBy(() -> + userService.createUser("", "User", "test@test.com", "a$$word")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Inga fält får lämnas tomma."); + } + + @Test + void testCreateUser_DuplicateEmail_ThrowsException() { + UserRepository mockRepository = mock(UserRepository.class); + UserService userService = new UserService(mockRepository); + + // Simulera att e-posten redan finns + when(mockRepository.findByEmail("test@test.com")).thenReturn(new User()); + + assertThatThrownBy(() -> + userService.createUser("Test", "User", "test@test.com", "a$$word")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("En användare med denna emailadress finns redan."); + } + + @Test + void testDeleteUserById_Success() { + UserRepository mockRepository = mock(UserRepository.class); + UserService userService = new UserService(mockRepository); + + // Mock successful delete + when(mockRepository.deleteUserById(1337L)).thenReturn(true); + + // Run through delete method in service + boolean result = userService.deleteUserById(1337L); + + // Make sure it was successful + assertThat(result).isTrue(); + // Verify that delete was called only once + verify(mockRepository, times(1)).deleteUserById(1337L); + } + + @Test + void testDeleteUserById_UserNotFound() { + UserRepository mockRepository = mock(UserRepository.class); + UserService userService = new UserService(mockRepository); + + // Mock failed delete + when(mockRepository.deleteUserById(1337L)).thenReturn(false); + + // Run through delete method in service + boolean result = userService.deleteUserById(1337L); + + // Make sure it failed + assertThat(result).isFalse(); + // Verify that delete was called only once + verify(mockRepository, times(1)).deleteUserById(1337L); + } + + @Test + void testDeleteUserByUsername_Success() { + UserRepository mockRepository = mock(UserRepository.class); + UserService userService = new UserService(mockRepository); + + // Mock successful delete + when(mockRepository.deleteUserByUsername("testuser")).thenReturn(true); + + boolean result = userService.deleteUserByUsername("testuser"); + + assertThat(result).isTrue(); + // Verify that delete was called only once + verify(mockRepository, times(1)).deleteUserByUsername("testuser"); + } + + @Test + void testDeleteUserByUsername_UserNotFound() { + UserRepository mockRepository = mock(UserRepository.class); + UserService userService = new UserService(mockRepository); + + // Mock return failed deletion + when(mockRepository.deleteUserByUsername("testuser")).thenReturn(false); + + boolean result = userService.deleteUserByUsername("testuser"); + + assertThat(result).isFalse(); + // Verify that delete was called only once + verify(mockRepository, times(1)).deleteUserByUsername("testuser"); + } + + @Test + void testFindByUsername_Found() { + UserRepository mockRepository = mock(UserRepository.class); + UserService userService = new UserService(mockRepository); + + User testUser = new User(); + testUser.setUsername("Waldo"); + + when(mockRepository.findByUsername("Waldo")).thenReturn(testUser); + + User result = userService.findByUsername("Waldo"); + + assertThat(result).isNotNull(); + assertThat(result.getUsername()).isEqualTo("Waldo"); + } + + @Test + void testFindByUsername_NotFound() { + UserRepository mockRepository = mock(UserRepository.class); + UserService userService = new UserService(mockRepository); + + when(mockRepository.findByUsername("Waldo")).thenReturn(null); + + User result = userService.findByUsername("Waldo"); + + assertThat(result).isNull(); + } + + @Test + void testFindAllUsers_ReturnsMultipleUsers() { + UserRepository mockRepository = mock(UserRepository.class); + UserService userService = new UserService(mockRepository); + + User user1 = new User(); + user1.setUsername("user1"); + + User user2 = new User(); + user2.setUsername("user2"); + + // Create a list of users + List testUsers = Arrays.asList(user1, user2); + + // Tell the mock to return the list of users + when(mockRepository.findAllUsers()).thenReturn(testUsers); + + // Run through find users method in service + List result = userService.findAllUsers(); + + assertThat(result).hasSize(2); + assertThat(result.get(0).getUsername()).isEqualTo("user1"); + assertThat(result.get(1).getUsername()).isEqualTo("user2"); + } + + @Test + void testFindAllUsers_ReturnsEmptyList() { + UserRepository mockRepository = mock(UserRepository.class); + UserService userService = new UserService(mockRepository); + + when(mockRepository.findAllUsers()).thenReturn(Collections.emptyList()); + + List result = userService.findAllUsers(); + + assertThat(result).isEmpty(); + } +} + +