From 8a6dde72012ba15247fe004ca50af95a625bd422 Mon Sep 17 00:00:00 2001 From: AdrianBalunan Date: Fri, 24 Apr 2026 09:26:05 +0200 Subject: [PATCH] Feat: UserDAOTest work --- .../team6/database/DAO/UserDAOTest.java | 521 +++++++++++++++++- 1 file changed, 520 insertions(+), 1 deletion(-) diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DAO/UserDAOTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DAO/UserDAOTest.java index 066fc9c..6075d22 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DAO/UserDAOTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DAO/UserDAOTest.java @@ -1,4 +1,523 @@ package ntnu.systemutvikling.team6.database.DAO; +import ntnu.systemutvikling.team6.database.DatabaseConnection; +import ntnu.systemutvikling.team6.models.registry.UserRegistry; +import ntnu.systemutvikling.team6.models.user.*; +import ntnu.systemutvikling.team6.security.PasswordHasher; +import org.junit.jupiter.api.*; +import org.mockito.*; + +import java.sql.*; +import java.time.LocalDate; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * Some tests made by Claude AI: Sonnet 4.6, on 24.04.206 + */ public class UserDAOTest { -} + + // --- Mocks --- + private DatabaseConnection mockDbConnection; + private Connection mockConn; + private PreparedStatement mockStmt; + private ResultSet mockRs; + + private UserDAO userDAO; + + @BeforeEach + void setUp() throws SQLException { + mockDbConnection = mock(DatabaseConnection.class); + mockConn = mock(Connection.class); + mockStmt = mock(PreparedStatement.class); + mockRs = mock(ResultSet.class); + + // Every test gets a fresh DAO wired to our fake connection + when(mockDbConnection.getMySqlConnection()).thenReturn(mockConn); + + userDAO = new UserDAO(mockDbConnection); + } + + // ---------------------------------------------------------------- + // getUserFromDBUuid() + // ---------------------------------------------------------------- + + @Test + void getUserFromDBUuid_returnsUserWhenFound() throws SQLException { + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeQuery()).thenReturn(mockRs); + when(mockRs.next()).thenReturn(true, false); + + String userId = UUID.randomUUID().toString(); + String messageId = UUID.randomUUID().toString(); + String charityId = UUID.randomUUID().toString(); + + when(mockRs.getString("UUID_User")).thenReturn(userId); + when(mockRs.getString("user_name")).thenReturn("Bob"); + when(mockRs.getString("user_email")).thenReturn("bob@example.com"); + when(mockRs.getString("user_password")).thenReturn("hashedpw"); + when(mockRs.getString("role")).thenReturn(Role.NORMAL_USER.toString()); + + when(mockRs.getString("isAnonymous")).thenReturn("false"); + when(mockRs.getBoolean("isAnonymous")).thenReturn(false); + when(mockRs.getString("language")).thenReturn(Language.ENGLISH.toString()); + when(mockRs.getBoolean("lightmode")).thenReturn(true); + + when(mockRs.getString("UUID_message")).thenReturn(messageId); + when(mockRs.getString("message_title")).thenReturn("Hello"); + when(mockRs.getString("message_content")).thenReturn("Some content"); + when(mockRs.getString("message_date")).thenReturn(LocalDate.now().toString()); + + when(mockRs.getString("UUID_charities")).thenReturn(charityId); + when(mockRs.getString("org_number")).thenReturn("9999"); + when(mockRs.getString("charity_name")).thenReturn("HelpOrg"); + when(mockRs.getString("charity_link")).thenReturn("helporg.com"); + when(mockRs.getString("status")).thenReturn("active"); + when(mockRs.getBoolean("pre_approved")).thenReturn(true); + when(mockRs.getString("description")).thenReturn("We help"); + when(mockRs.getString("logoURL")).thenReturn(null); + when(mockRs.getString("key_values")).thenReturn(null); + when(mockRs.getBytes("logoBLOB")).thenReturn(null); + + User user = userDAO.getUserFromDBUuid(userId); + + assertNotNull(user); + assertEquals(userId, user.getId().toString()); + assertEquals("Bob", user.getUsername()); + assertEquals(1, user.getInbox().getMessages().size()); + assertEquals("Hello", user.getInbox().getMessages().getFirst().getTitle()); + assertEquals(charityId, user.getInbox().getMessages().getFirst().getFrom().getUUID().toString()); + } + + @Test + void getUserFromDBUuid_returnsNullWhenNotFound() throws SQLException { + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeQuery()).thenReturn(mockRs); + when(mockRs.next()).thenReturn(false); // no rows + + User user = userDAO.getUserFromDBUuid(UUID.randomUUID().toString()); + + assertNull(user); + } + @Test + void getUserFromDBUuid_throwsRuntimeExceptionOnSQLException() throws SQLException { + when(mockConn.prepareStatement(anyString())) + .thenThrow(new SQLException("Connection lost")); + + assertThrows(RuntimeException.class, + () -> userDAO.getUserFromDBUuid(UUID.randomUUID().toString())); + } + + @Test + void getUserFromDBUuid_doesNotDuplicateMessagesAcrossRows() throws SQLException { + // Same message ID appearing in two rows should only produce one Message in the inbox + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeQuery()).thenReturn(mockRs); + when(mockRs.next()).thenReturn(true, true, false); + + String userId = UUID.randomUUID().toString(); + String messageId = UUID.randomUUID().toString(); // same ID both rows + String charityId = UUID.randomUUID().toString(); + + when(mockRs.getString("UUID_User")).thenReturn(userId); + when(mockRs.getString("user_name")).thenReturn("Bob"); + when(mockRs.getString("user_email")).thenReturn("bob@example.com"); + when(mockRs.getString("user_password")).thenReturn("hashedpw"); + when(mockRs.getString("role")).thenReturn(Role.NORMAL_USER.toString()); + when(mockRs.getString("isAnonymous")).thenReturn("false"); + when(mockRs.getBoolean("isAnonymous")).thenReturn(false); + when(mockRs.getString("language")).thenReturn(Language.ENGLISH.toString()); + when(mockRs.getBoolean("lightmode")).thenReturn(false); + when(mockRs.getString("UUID_message")).thenReturn(messageId); + when(mockRs.getString("message_title")).thenReturn("Title"); + when(mockRs.getString("message_content")).thenReturn("Content"); + when(mockRs.getString("message_date")).thenReturn(LocalDate.now().toString()); + when(mockRs.getString("UUID_charities")).thenReturn(charityId); + when(mockRs.getString("org_number")).thenReturn("1234"); + when(mockRs.getString("charity_name")).thenReturn("Org"); + when(mockRs.getString("charity_link")).thenReturn("org.com"); + when(mockRs.getString("status")).thenReturn("active"); + when(mockRs.getBoolean("pre_approved")).thenReturn(false); + when(mockRs.getString("description")).thenReturn("desc"); + when(mockRs.getString("logoURL")).thenReturn(null); + when(mockRs.getString("key_values")).thenReturn(null); + when(mockRs.getBytes("logoBLOB")).thenReturn(null); + + User user = userDAO.getUserFromDBUuid(userId); + + assertEquals(1, user.getInbox().getMessages().size()); + } + + // ---------------------------------------------------------------- + // isEmailTaken() + // ---------------------------------------------------------------- + + @Test + void isEmailTaken_returnsTrueWhenEmailExists() throws SQLException { + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeQuery()).thenReturn(mockRs); + when(mockRs.next()).thenReturn(true); // simulates a row found + + assertTrue(userDAO.isEmailTaken("test@example.com")); + } + + @Test + void isEmailTaken_returnsFalseWhenEmailDoesNotExist() throws SQLException { + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeQuery()).thenReturn(mockRs); + when(mockRs.next()).thenReturn(false); // no row → email is free + + assertFalse(userDAO.isEmailTaken("new@example.com")); + } + + @Test + void isEmailTaken_throwsOnInvalidEmail() { + // No DB call should happen — the guard clause throws immediately + assertThrows(IllegalArgumentException.class, + () -> userDAO.isEmailTaken("notanemail")); + + assertThrows(IllegalArgumentException.class, + () -> userDAO.isEmailTaken(null)); + + assertThrows(IllegalArgumentException.class, + () -> userDAO.isEmailTaken(" ")); + } + + // ---------------------------------------------------------------- + // getUsersFromDB() + // ---------------------------------------------------------------- + + @Test + void getUsersFromDB_returnsAllUsers() throws SQLException { + Statement mockStatement = mock(Statement.class); + when(mockConn.createStatement()).thenReturn(mockStatement); + when(mockStatement.executeQuery(anyString())).thenReturn(mockRs); + + String userId1 = UUID.randomUUID().toString(); + String userId2 = UUID.randomUUID().toString(); + + // Two distinct users, no messages to keep stubbing simple + when(mockRs.next()).thenReturn(true, true, false); + when(mockRs.getString("UUID_User")).thenReturn(userId1, userId2); + when(mockRs.getString("user_name")).thenReturn("Alice", "Bob"); + when(mockRs.getString("user_email")).thenReturn("alice@example.com", "bob@example.com"); + when(mockRs.getString("user_password")).thenReturn("hash1", "hash2"); + when(mockRs.getString("role")).thenReturn(Role.NORMAL_USER.toString()); + when(mockRs.getString("isAnonymous")).thenReturn(null); // no settings row + when(mockRs.getString("UUID_message")).thenReturn(null); // no messages + + UserRegistry registry = userDAO.getUsersFromDB(); + + assertNotNull(registry); + assertEquals(2, registry.getAllUsers().size()); + } + + @Test + void getUsersFromDB_returnsEmptyRegistryWhenNoUsers() throws SQLException { + Statement mockStatement = mock(Statement.class); + when(mockConn.createStatement()).thenReturn(mockStatement); + when(mockStatement.executeQuery(anyString())).thenReturn(mockRs); + when(mockRs.next()).thenReturn(false); + + UserRegistry registry = userDAO.getUsersFromDB(); + + assertNotNull(registry); + assertTrue(registry.getAllUsers().isEmpty()); + } + + @Test + void getUsersFromDB_throwsRuntimeExceptionOnSQLException() throws SQLException { + when(mockConn.createStatement()).thenThrow(new SQLException("DB down")); + + assertThrows(RuntimeException.class, () -> userDAO.getUsersFromDB()); + } + + // ---------------------------------------------------------------- + // getSettingsForUser() + // ---------------------------------------------------------------- + + @Test + void getSettingsForUser_returnsSettingsWhenFound() throws SQLException { + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeQuery()).thenReturn(mockRs); + when(mockRs.next()).thenReturn(true, false); + when(mockRs.getBoolean("isAnonymous")).thenReturn(true); + when(mockRs.getString("language")).thenReturn(Language.ENGLISH.toString()); + when(mockRs.getBoolean("lightmode")).thenReturn(false); + + Settings settings = userDAO.getSettingsForUser(UUID.randomUUID().toString()); + + assertNotNull(settings); + assertTrue(settings.isAnonymous()); + assertEquals(Language.ENGLISH, settings.getLanguage()); + assertFalse(settings.isLightMode()); + } + + @Test + void getSettingsForUser_returnsNullWhenNotFound() throws SQLException { + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeQuery()).thenReturn(mockRs); + when(mockRs.next()).thenReturn(false); + + Settings settings = userDAO.getSettingsForUser(UUID.randomUUID().toString()); + + assertNull(settings); + } + + @Test + void getSettingsForUser_appliesMaxRowsLimit() throws SQLException { + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeQuery()).thenReturn(mockRs); + when(mockRs.next()).thenReturn(false); + + userDAO.getSettingsForUser(UUID.randomUUID().toString()); + + // The DAO must call setMaxRows(1) to guard against multiple settings rows + verify(mockStmt).setMaxRows(1); + } + + @Test + void getSettingsForUser_throwsRuntimeExceptionOnSQLException() throws SQLException { + when(mockConn.prepareStatement(anyString())) + .thenThrow(new SQLException("DB error")); + + assertThrows(RuntimeException.class, + () -> userDAO.getSettingsForUser(UUID.randomUUID().toString())); + } + + // ---------------------------------------------------------------- + // updateUserDetails() + // ---------------------------------------------------------------- + + @Test + void updateUserDetails_returnsTrueOnSuccess() throws SQLException { + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeUpdate()).thenReturn(1); + + User user = buildTestUser(); + assertTrue(userDAO.updateUserDetails(user)); + + // Verify the three columns are set in the correct parameter order + verify(mockStmt).setString(1, user.getUsername()); + verify(mockStmt).setString(2, user.getEmail()); + verify(mockStmt).setString(3, user.getPasswordHash()); + verify(mockStmt).setString(4, user.getId().toString()); + } + + @Test + void updateUserDetails_returnsFalseWhenNoRowsAffected() throws SQLException { + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeUpdate()).thenReturn(0); + + assertFalse(userDAO.updateUserDetails(buildTestUser())); + } + + @Test + void updateUserDetails_returnsFalseOnSQLException() throws SQLException { + when(mockConn.prepareStatement(anyString())) + .thenThrow(new SQLException("Update failed")); + + assertFalse(userDAO.updateUserDetails(buildTestUser())); + } + + // ---------------------------------------------------------------- + // getInboxForUser() + // ---------------------------------------------------------------- + + @Test + void getInboxForUser_returnsInboxWithMessages() throws SQLException { + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeQuery()).thenReturn(mockRs); + when(mockRs.next()).thenReturn(true, false); + + String charityId = UUID.randomUUID().toString(); + when(mockRs.getString("UUID_charities")).thenReturn(charityId); + when(mockRs.getString("org_number")).thenReturn("5678"); + when(mockRs.getString("charity_name")).thenReturn("SaveAll"); + when(mockRs.getString("charity_link")).thenReturn("saveall.org"); + when(mockRs.getString("status")).thenReturn("active"); + when(mockRs.getBoolean("pre_approved")).thenReturn(false); + when(mockRs.getString("description")).thenReturn("We save"); + when(mockRs.getString("logoURL")).thenReturn(null); + when(mockRs.getString("key_values")).thenReturn(null); + when(mockRs.getBytes("logoBLOB")).thenReturn(null); + when(mockRs.getString("message_title")).thenReturn("Update"); + when(mockRs.getString("message_content")).thenReturn("Big news"); + when(mockRs.getString("message_date")).thenReturn(LocalDate.now().toString()); + + Inbox inbox = userDAO.getInboxForUser(UUID.randomUUID().toString()); + + assertNotNull(inbox); + assertEquals(1, inbox.getMessages().size()); + assertEquals("Update", inbox.getMessages().getFirst().getTitle()); + assertEquals(charityId, inbox.getMessages().getFirst().getFrom().getUUID().toString()); + } + + @Test + void getInboxForUser_returnsEmptyInboxWhenNoMessages() throws SQLException { + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeQuery()).thenReturn(mockRs); + when(mockRs.next()).thenReturn(false); + + Inbox inbox = userDAO.getInboxForUser(UUID.randomUUID().toString()); + + assertNotNull(inbox); + assertTrue(inbox.getMessages().isEmpty()); + } + + @Test + void getInboxForUser_throwsRuntimeExceptionOnSQLException() throws SQLException { + when(mockConn.prepareStatement(anyString())) + .thenThrow(new SQLException("Timeout")); + + assertThrows(RuntimeException.class, + () -> userDAO.getInboxForUser(UUID.randomUUID().toString())); + } + + // ---------------------------------------------------------------- + // registerUser() + // ---------------------------------------------------------------- + + @Test + void registerUser_returnsTrueOnSuccess() throws SQLException { + // Two prepared statements are created (User insert + Settings insert) + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeUpdate()).thenReturn(1); // 1 row affected each time + + User user = buildTestUser(); + assertTrue(userDAO.registerUser(user)); + + // Both inserts should have been executed + verify(mockStmt, times(2)).executeUpdate(); + // Auto-commit should have been disabled and commit called + verify(mockConn).setAutoCommit(false); + verify(mockConn).commit(); + } + + @Test + void registerUser_returnsFalseWhenInsertAffectsNoRows() throws SQLException { + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeUpdate()).thenReturn(0); // nothing inserted + + assertFalse(userDAO.registerUser(buildTestUser())); + } + + @Test + void registerUser_returnsFalseOnSQLException() throws SQLException { + when(mockConn.prepareStatement(anyString())) + .thenThrow(new SQLException("DB down")); + + assertFalse(userDAO.registerUser(buildTestUser())); + } + + // ---------------------------------------------------------------- + // getUserFromDBEmailAndPassword() + // ---------------------------------------------------------------- + + @Test + void getUserFromDB_returnsNullWhenPasswordDoesNotMatch() throws SQLException { + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeQuery()).thenReturn(mockRs); + + when(mockRs.next()).thenReturn(true, false); + when(mockRs.getString("UUID_User")).thenReturn("some-uuid"); + when(mockRs.getString("user_name")).thenReturn("Alice"); + when(mockRs.getString("user_password")).thenReturn(new PasswordHasher().getHashPassword("differentPassword")); + + User result = userDAO.getUserFromDBEmailAndPassword("alice@example.com", "wrongPassword"); + + assertNull(result); + } + + @Test + void getUserFromDB_throwsRuntimeExceptionOnSQLException() throws SQLException { + when(mockConn.prepareStatement(anyString())) + .thenThrow(new SQLException("Connection lost")); + + assertThrows(RuntimeException.class, + () -> userDAO.getUserFromDBEmailAndPassword("a@b.com", "pass")); + } + + @Test + void getUserFromDB_returnsAUserWhenCorrect() throws SQLException { + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeQuery()).thenReturn(mockRs); + when(mockRs.next()).thenReturn(true, false); + String userId = UUID.randomUUID().toString(); + when(mockRs.getString("UUID_User")).thenReturn(userId); + when(mockRs.getString("user_name")).thenReturn("name"); + when(mockRs.getString("user_email")).thenReturn("a@b.com"); + when(mockRs.getString("user_password")).thenReturn(new PasswordHasher().getHashPassword("somePassword")); + when(mockRs.getString("role")).thenReturn(Role.NORMAL_USER.toString()); + when(mockRs.getBoolean("isAnonymous")).thenReturn(false); + when(mockRs.getString("language")).thenReturn(Language.ENGLISH.toString()); + when(mockRs.getBoolean("lightmode")).thenReturn(false); + String messageId = UUID.randomUUID().toString(); + when(mockRs.getString("message_title")).thenReturn("Title"); + when(mockRs.getString("UUID_message")).thenReturn(messageId); + when(mockRs.getString("message_content")).thenReturn("blah blah blah"); + when(mockRs.getString("message_date")).thenReturn(LocalDate.now().toString()); + + String charityId = UUID.randomUUID().toString(); + when(mockRs.getString("org_number")).thenReturn("1234"); + when(mockRs.getString("charity_name")).thenReturn("charity"); + when(mockRs.getString("charity_link")).thenReturn("link.com"); + when(mockRs.getString("status")).thenReturn("Something"); + when(mockRs.getString("UUID_charities")).thenReturn(charityId); + when(mockRs.getString("description")).thenReturn(charityId); + when(mockRs.getString("logoURL")).thenReturn(null); + when(mockRs.getString("key_values")).thenReturn(null); + when(mockRs.getBytes("logoBLOB")).thenReturn(null); + + User user = userDAO.getUserFromDBEmailAndPassword("a@b.com", "somePassword"); + + assertEquals(userId, user.getId().toString()); + assertEquals("Title", user.getInbox().getMessages().getFirst().getTitle()); + assertEquals(charityId, user.getInbox().getMessages().getFirst().getFrom().getUUID().toString()); + } + + // ---------------------------------------------------------------- + // updateUserSettings() + // ---------------------------------------------------------------- + + @Test + void updateUserSettings_returnsTrueOnSuccess() throws SQLException { + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + when(mockStmt.executeUpdate()).thenReturn(1); + + User user = buildTestUser(); + Settings newSettings = new Settings(true, Language.ENGLISH, false); + + assertTrue(userDAO.updateUserSettings(user, newSettings)); + verify(mockStmt).setBoolean(1, true); // isAnonymous + verify(mockStmt).setBoolean(3, false); // lightmode + } + + @Test + void updateUserSettings_returnsFalseOnSQLException() throws SQLException { + when(mockConn.prepareStatement(anyString())) + .thenThrow(new SQLException("Timeout")); + + assertFalse(userDAO.updateUserSettings(buildTestUser(), new Settings(false, Language.ENGLISH, true))); + } + + // ---------------------------------------------------------------- + // Helper + // ---------------------------------------------------------------- + + private User buildTestUser() { + Settings settings = new Settings(false, Language.ENGLISH, true); + User user = new User( + UUID.randomUUID().toString(), + "TestUser", + "test@example.com", + "hashedpassword123", + Role.NORMAL_USER.toString() + ); + user.setSettings(settings); + user.setInbox(new Inbox()); + return user; + } +} \ No newline at end of file