diff --git a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DAO/CharityDAOTest.java b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DAO/CharityDAOTest.java index 4d5cf8b..17da250 100644 --- a/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DAO/CharityDAOTest.java +++ b/helpmehelpapplication/src/test/java/ntnu/systemutvikling/team6/database/DAO/CharityDAOTest.java @@ -1,4 +1,339 @@ package ntnu.systemutvikling.team6.database.DAO; +import ntnu.systemutvikling.team6.database.DatabaseConnection; +import ntnu.systemutvikling.team6.models.Charity; +import ntnu.systemutvikling.team6.models.Feedback; +import ntnu.systemutvikling.team6.models.registry.CharityRegistry; +import ntnu.systemutvikling.team6.models.user.Language; +import ntnu.systemutvikling.team6.models.user.Role; +import org.junit.jupiter.api.*; +import java.sql.*; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.UUID; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + public class CharityDAOTest { -} + + // --- Mocks --- + private DatabaseConnection mockDbConnection; + private Connection mockConn; + private Statement mockRawStmt; // getCharitiesFromDB() uses createStatement() + private PreparedStatement mockStmt; // getFeedbackForCharityUUID() uses prepareStatement() + private ResultSet mockRs; + + private CharityDAO charityDAO; + + @BeforeEach + void setUp() throws SQLException { + mockDbConnection = mock(DatabaseConnection.class); + mockConn = mock(Connection.class); + mockRawStmt = mock(Statement.class); + mockStmt = mock(PreparedStatement.class); + mockRs = mock(ResultSet.class); + + when(mockDbConnection.getMySqlConnection()).thenReturn(mockConn); + when(mockConn.createStatement()).thenReturn(mockRawStmt); + when(mockConn.prepareStatement(anyString())).thenReturn(mockStmt); + + charityDAO = new CharityDAO(mockDbConnection); + } + + // ---------------------------------------------------------------- + // Helpers + // ---------------------------------------------------------------- + + /** Stubs the full set of charity columns for a single row. */ + private void stubCharityRow(String charityId) throws SQLException { + when(mockRs.getString("UUID_charities")).thenReturn(charityId); + when(mockRs.getString("org_number")).thenReturn("123456789"); + 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 people"); + when(mockRs.getString("logoURL")).thenReturn(null); + when(mockRs.getString("key_values")).thenReturn(null); + when(mockRs.getBytes("logoBLOB")).thenReturn(null); + when(mockRs.getString("category")).thenReturn(null); + when(mockRs.getString("UUID_feedback")).thenReturn(null); // no feedback by default + } + + /** Stubs a feedback + user block on top of an already-stubbed charity row. */ + private void stubFeedbackRow(String feedbackId, String userId) throws SQLException { + when(mockRs.getString("UUID_feedback")).thenReturn(feedbackId); + when(mockRs.getString("feedback_comment")).thenReturn("Great charity!"); + when(mockRs.getString("feedback_date")).thenReturn(LocalDate.now().toString()); + when(mockRs.getString("UUID_User")).thenReturn(userId); + when(mockRs.getString("user_name")).thenReturn("Alice"); + when(mockRs.getString("user_email")).thenReturn("alice@example.com"); + when(mockRs.getString("user_password")).thenReturn("hashedpw"); + when(mockRs.getString("role")).thenReturn(Role.NORMAL_USER.toString()); + } + + // ---------------------------------------------------------------- + // getCharitiesFromDB() — uses createStatement() + // ---------------------------------------------------------------- + + @Test + void getCharitiesFromDB_returnsRegistryWithOneCharity() throws SQLException { + when(mockRawStmt.executeQuery(anyString())).thenReturn(mockRs); + when(mockRs.next()).thenReturn(true, false); + + String charityId = UUID.randomUUID().toString(); + stubCharityRow(charityId); + + CharityRegistry registry = charityDAO.getCharitiesFromDB(); + + assertNotNull(registry); + assertEquals(1, registry.getAllCharities().size()); + assertEquals(charityId, registry.getAllCharities().getFirst().getUUID().toString()); + assertEquals("HelpOrg", registry.getAllCharities().getFirst().getName()); + } + + @Test + void getCharitiesFromDB_returnsEmptyRegistryWhenNoRows() throws SQLException { + when(mockRawStmt.executeQuery(anyString())).thenReturn(mockRs); + when(mockRs.next()).thenReturn(false); + + CharityRegistry registry = charityDAO.getCharitiesFromDB(); + + assertNotNull(registry); + assertTrue(registry.getAllCharities().isEmpty()); + } + + @Test + void getCharitiesFromDB_doesNotDuplicateCharityAcrossMultipleRows() throws SQLException { + // Same charity appearing in two rows (e.g. two categories) → only one Charity object + when(mockRawStmt.executeQuery(anyString())).thenReturn(mockRs); + when(mockRs.next()).thenReturn(true, true, false); + + String charityId = UUID.randomUUID().toString(); + when(mockRs.getString("UUID_charities")).thenReturn(charityId); + when(mockRs.getString("org_number")).thenReturn("111"); + when(mockRs.getString("charity_name")).thenReturn("EduOrg"); + when(mockRs.getString("charity_link")).thenReturn("eduorg.com"); + when(mockRs.getString("status")).thenReturn("active"); + when(mockRs.getBoolean("pre_approved")).thenReturn(false); + when(mockRs.getString("description")).thenReturn("Education"); + when(mockRs.getString("logoURL")).thenReturn(null); + when(mockRs.getString("key_values")).thenReturn(null); + when(mockRs.getBytes("logoBLOB")).thenReturn(null); + when(mockRs.getString("category")).thenReturn("Education", "Youth"); + when(mockRs.getString("UUID_feedback")).thenReturn(null); + + CharityRegistry registry = charityDAO.getCharitiesFromDB(); + + assertEquals(1, registry.getAllCharities().size()); + assertEquals(2, registry.getAllCharities().getFirst().getCategory().size()); + assertTrue(registry.getAllCharities().getFirst().getCategory().contains("Education")); + assertTrue(registry.getAllCharities().getFirst().getCategory().contains("Youth")); + } + + @Test + void getCharitiesFromDB_returnsMultipleDistinctCharities() throws SQLException { + when(mockRawStmt.executeQuery(anyString())).thenReturn(mockRs); + when(mockRs.next()).thenReturn(true, true, false); + + String charityId1 = UUID.randomUUID().toString(); + String charityId2 = UUID.randomUUID().toString(); + + when(mockRs.getString("UUID_charities")).thenReturn(charityId1, charityId2); + when(mockRs.getString("org_number")).thenReturn("111", "222"); + when(mockRs.getString("charity_name")).thenReturn("OrgA", "OrgB"); + when(mockRs.getString("charity_link")).thenReturn("orga.com", "orgb.com"); + when(mockRs.getString("status")).thenReturn("active"); + when(mockRs.getBoolean("pre_approved")).thenReturn(true); + 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); + when(mockRs.getString("category")).thenReturn(null); + when(mockRs.getString("UUID_feedback")).thenReturn(null); + + CharityRegistry registry = charityDAO.getCharitiesFromDB(); + + assertEquals(2, registry.getAllCharities().size()); + } + + @Test + void getCharitiesFromDB_appendsFeedbackToCharity() throws SQLException { + when(mockRawStmt.executeQuery(anyString())).thenReturn(mockRs); + when(mockRs.next()).thenReturn(true, false); + + String charityId = UUID.randomUUID().toString(); + String feedbackId = UUID.randomUUID().toString(); + String userId = UUID.randomUUID().toString(); + + stubCharityRow(charityId); + stubFeedbackRow(feedbackId, userId); + + CharityRegistry registry = charityDAO.getCharitiesFromDB(); + + Charity charity = registry.getAllCharities().getFirst(); + assertEquals(1, charity.getFeedbacks().size()); + assertEquals(feedbackId, charity.getFeedbacks().getFirst().getFeedbackId().toString()); + assertEquals("Great charity!", charity.getFeedbacks().getFirst().getComment()); + assertEquals(userId, charity.getFeedbacks().getFirst().getUser().getId().toString()); + } + + @Test + void getCharitiesFromDB_doesNotDuplicateFeedbackAcrossRows() throws SQLException { + // Same feedbackId appearing in two rows (e.g. two categories) → only one Feedback added + when(mockRawStmt.executeQuery(anyString())).thenReturn(mockRs); + when(mockRs.next()).thenReturn(true, true, false); + + String charityId = UUID.randomUUID().toString(); + String feedbackId = UUID.randomUUID().toString(); + String userId = UUID.randomUUID().toString(); + + when(mockRs.getString("UUID_charities")).thenReturn(charityId); + when(mockRs.getString("org_number")).thenReturn("111"); + 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); + when(mockRs.getString("category")).thenReturn("Health", "Health"); + stubFeedbackRow(feedbackId, userId); // same feedbackId both rows + + CharityRegistry registry = charityDAO.getCharitiesFromDB(); + + assertEquals(1, registry.getAllCharities().getFirst().getFeedbacks().size()); + } + + @Test + void getCharitiesFromDB_clearsSeenFeedbackIdsWhenNewCharityStarts() throws SQLException { + // feedbackId seen on charity1 must NOT be deduplicated against charity2 + when(mockRawStmt.executeQuery(anyString())).thenReturn(mockRs); + when(mockRs.next()).thenReturn(true, true, false); + + String charityId1 = UUID.randomUUID().toString(); + String charityId2 = UUID.randomUUID().toString(); + String feedbackId = UUID.randomUUID().toString(); // same UUID reused on both charities + String userId = UUID.randomUUID().toString(); + + when(mockRs.getString("UUID_charities")).thenReturn(charityId1, charityId2); + when(mockRs.getString("org_number")).thenReturn("111", "222"); + when(mockRs.getString("charity_name")).thenReturn("OrgA", "OrgB"); + when(mockRs.getString("charity_link")).thenReturn("a.com", "b.com"); + when(mockRs.getString("status")).thenReturn("active"); + when(mockRs.getBoolean("pre_approved")).thenReturn(true); + 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); + when(mockRs.getString("category")).thenReturn(null); + when(mockRs.getString("UUID_feedback")).thenReturn(feedbackId); + when(mockRs.getString("feedback_comment")).thenReturn("Good"); + when(mockRs.getString("feedback_date")).thenReturn(LocalDate.now().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("pw"); + when(mockRs.getString("role")).thenReturn(Role.NORMAL_USER.toString()); + + CharityRegistry registry = charityDAO.getCharitiesFromDB(); + + // Each charity should have its own feedback entry + assertEquals(1, registry.getAllCharities().get(0).getFeedbacks().size()); + assertEquals(1, registry.getAllCharities().get(1).getFeedbacks().size()); + } + + @Test + void getCharitiesFromDB_throwsRuntimeExceptionOnSQLException() throws SQLException { + when(mockConn.createStatement()).thenThrow(new SQLException("DB down")); + + assertThrows(RuntimeException.class, () -> charityDAO.getCharitiesFromDB()); + } + + // ---------------------------------------------------------------- + // getFeedbackForCharityUUID() — uses prepareStatement() + // ---------------------------------------------------------------- + + @Test + void getFeedbackForCharityUUID_returnsFeedbackList() throws SQLException { + when(mockStmt.executeQuery()).thenReturn(mockRs); + when(mockRs.next()).thenReturn(true, false); + + String feedbackId = UUID.randomUUID().toString(); + String userId = UUID.randomUUID().toString(); + + when(mockRs.getString("UUID_feedback")).thenReturn(feedbackId); + when(mockRs.getString("feedback_comment")).thenReturn("Very helpful"); + when(mockRs.getString("feedback_date")).thenReturn(LocalDate.now().toString()); + when(mockRs.getBoolean("isAnonymous")).thenReturn(false); + when(mockRs.getString("UUID_User")).thenReturn(userId); + when(mockRs.getString("user_name")).thenReturn("Carol"); + when(mockRs.getString("user_email")).thenReturn("carol@example.com"); + when(mockRs.getString("user_password")).thenReturn("pw"); + when(mockRs.getString("role")).thenReturn(Role.NORMAL_USER.toString()); + when(mockRs.getString("language")).thenReturn(Language.ENGLISH.toString()); + when(mockRs.getBoolean("lightmode")).thenReturn(false); + + ArrayList result = charityDAO.getFeedbackforCharityUUID(UUID.randomUUID().toString()); + + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(feedbackId, result.getFirst().getFeedbackId().toString()); + assertEquals("Very helpful", result.getFirst().getComment()); + assertEquals(userId, result.getFirst().getUser().getId().toString()); + } + + @Test + void getFeedbackForCharityUUID_returnsEmptyListWhenNoFeedback() throws SQLException { + when(mockStmt.executeQuery()).thenReturn(mockRs); + when(mockRs.next()).thenReturn(false); + + ArrayList result = charityDAO.getFeedbackforCharityUUID(UUID.randomUUID().toString()); + + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + void getFeedbackForCharityUUID_returnsMultipleEntries() throws SQLException { + when(mockStmt.executeQuery()).thenReturn(mockRs); + when(mockRs.next()).thenReturn(true, true, false); + + when(mockRs.getString("UUID_feedback")).thenReturn( + UUID.randomUUID().toString(), UUID.randomUUID().toString()); + when(mockRs.getString("feedback_comment")).thenReturn("Good", "Excellent"); + when(mockRs.getString("feedback_date")).thenReturn(LocalDate.now().toString()); + when(mockRs.getBoolean("isAnonymous")).thenReturn(false); + when(mockRs.getString("UUID_User")).thenReturn(UUID.randomUUID().toString()); + when(mockRs.getString("user_name")).thenReturn("User"); + when(mockRs.getString("user_email")).thenReturn("u@example.com"); + when(mockRs.getString("user_password")).thenReturn("pw"); + when(mockRs.getString("role")).thenReturn(Role.NORMAL_USER.toString()); + when(mockRs.getString("language")).thenReturn(Language.ENGLISH.toString()); + when(mockRs.getBoolean("lightmode")).thenReturn(false); + + ArrayList result = charityDAO.getFeedbackforCharityUUID(UUID.randomUUID().toString()); + + assertEquals(2, result.size()); + } + + @Test + void getFeedbackForCharityUUID_passesCorrectCharityIdToQuery() throws SQLException { + when(mockStmt.executeQuery()).thenReturn(mockRs); + when(mockRs.next()).thenReturn(false); + + String charityId = UUID.randomUUID().toString(); + charityDAO.getFeedbackforCharityUUID(charityId); + + verify(mockStmt).setString(1, charityId); + } + + @Test + void getFeedbackForCharityUUID_throwsRuntimeExceptionOnSQLException() throws SQLException { + when(mockStmt.executeQuery()).thenThrow(new SQLException("Query failed")); + + assertThrows(RuntimeException.class, + () -> charityDAO.getFeedbackforCharityUUID(UUID.randomUUID().toString())); + } +} \ No newline at end of file