Skip to content

Commit

Permalink
Feat: Added DonationSelect JavaDoc And Junit test
Browse files Browse the repository at this point in the history
  • Loading branch information
AdrianBalunan committed Apr 12, 2026
1 parent f6d44f7 commit c1c1b7f
Show file tree
Hide file tree
Showing 2 changed files with 246 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,41 @@
import ntnu.systemutvikling.team6.models.Donation;
import ntnu.systemutvikling.team6.models.DonationRegistry;

/**
* Data access class responsible for reading donation data from the database.
*
* <p>Retrieves donations along with their associated charity information by
* performing an INNER JOIN between the {@code Donations} and {@code Charities} tables.
* Only donations that have a matching charity record are returned.</p>
*/

public class DonationSelect {

/** The database connection used for all queries in this class. */
private final DatabaseConnection connection;

/**
* Constructs a new {@code DonationSelect} with the given database connection.
*
* @param connection the {@link DatabaseConnection} to use for executing queries;
* must not be {@code null}
*/
public DonationSelect(DatabaseConnection connection) {
this.connection = connection;
}

/**
* Retrieves all donations from the database, each populated with its associated
* {@link Charity}.
*
* <p>The query performs an INNER JOIN between the {@code Donations} and
* {@code Charities} tables on the charity UUID foreign key. Donations without a
* matching charity are excluded from the result.</p>
*
* @return a {@link DonationRegistry} containing all matched donations;
* never {@code null}, but may be empty if no rows are returned
* @throws RuntimeException if a {@link SQLException} occurs while executing the query
*/
public DonationRegistry getDonationFromDB() {
DonationRegistry registry = null;
Connection conn = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package ntnu.systemutvikling.team6.database.Readers;

import ntnu.systemutvikling.team6.database.DatabaseConnection;
import ntnu.systemutvikling.team6.models.Donation;
import ntnu.systemutvikling.team6.models.DonationRegistry;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.sql.*;
import java.time.LocalDate;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;

/**
* Unit tests for {@link DonationSelect}.
*
* <p>Uses Mockito to mock the entire JDBC stack so no real database
* connection is required.</p>
*/
@ExtendWith(MockitoExtension.class)
class DonationSelectTest {

@Mock private DatabaseConnection mockDatabaseConnection;
@Mock private Connection mockConnection;
@Mock private Statement mockStatement;
@Mock private ResultSet mockResultSet;
@Mock private Date mockSqlDate;

private DonationSelect donationSelect;

@BeforeEach
void setUp() {
donationSelect = new DonationSelect(mockDatabaseConnection);
}

// -------------------------------------------------------------------------
// getDonationFromDB
// -------------------------------------------------------------------------

@Test
@DisplayName("getDonationFromDB – empty result set returns an empty registry")
void getDonationFromDB_emptyResultSet_returnsEmptyRegistry() throws Exception {
when(mockDatabaseConnection.getMySqlConnection()).thenReturn(mockConnection);
when(mockConnection.createStatement()).thenReturn(mockStatement);
when(mockStatement.executeQuery(anyString())).thenReturn(mockResultSet);
when(mockResultSet.next()).thenReturn(false);

DonationRegistry registry = donationSelect.getDonationFromDB();

assertNotNull(registry);
assertTrue(registry.getDonations().isEmpty(),
"Registry should be empty when the result set has no rows");
}

@Test
@DisplayName("getDonationFromDB – single row returns one Donation with correct data")
void getDonationFromDB_singleRow_returnsSingleDonation() throws Exception {
when(mockDatabaseConnection.getMySqlConnection()).thenReturn(mockConnection);
when(mockConnection.createStatement()).thenReturn(mockStatement);
when(mockStatement.executeQuery(anyString())).thenReturn(mockResultSet);

when(mockResultSet.next()).thenReturn(true, false);
stubCharityColumns("charity-uuid-1", "123456789", "Test Charity",
"https://example.org", true, "ACTIVE");
stubDonationColumns("donation-uuid-1", 250.0, LocalDate.of(2024, 5, 20));

DonationRegistry registry = donationSelect.getDonationFromDB();

assertEquals(1, registry.getDonations().size());
Donation donation = registry.getDonations().get(0);
assertEquals("donation-uuid-1", donation.getUUID());
assertEquals(250.0, donation.getAmount());
assertEquals(LocalDate.of(2024, 5, 20), donation.getDate());
}

@Test
@DisplayName("getDonationFromDB – single row maps charity fields onto the Donation correctly")
void getDonationFromDB_singleRow_charityMappedCorrectly() throws Exception {
when(mockDatabaseConnection.getMySqlConnection()).thenReturn(mockConnection);
when(mockConnection.createStatement()).thenReturn(mockStatement);
when(mockStatement.executeQuery(anyString())).thenReturn(mockResultSet);

when(mockResultSet.next()).thenReturn(true, false);
stubCharityColumns("charity-uuid-1", "987654321", "Help Fund",
"https://helpfund.org", false, "PENDING");
stubDonationColumns("donation-uuid-1", 100.0, LocalDate.of(2024, 1, 1));

DonationRegistry registry = donationSelect.getDonationFromDB();

Donation donation = registry.getDonations().get(0);
assertEquals("charity-uuid-1", donation.getCharity().getUUID());
assertEquals("987654321", donation.getCharity().getOrgNumber());
assertEquals("Help Fund", donation.getCharity().getCharityName());
assertEquals("https://helpfund.org", donation.getCharity().getCharityLink());
assertFalse(donation.getCharity().isPreApproved());
assertEquals("PENDING", donation.getCharity().getStatus());
}

@Test
@DisplayName("getDonationFromDB – two rows returns two Donation objects")
void getDonationFromDB_twoRows_returnsTwoDonations() throws Exception {
when(mockDatabaseConnection.getMySqlConnection()).thenReturn(mockConnection);
when(mockConnection.createStatement()).thenReturn(mockStatement);
when(mockStatement.executeQuery(anyString())).thenReturn(mockResultSet);

when(mockResultSet.next()).thenReturn(true, true, false);

when(mockResultSet.getString("UUID_charities")).thenReturn("charity-uuid-1", "charity-uuid-2");
when(mockResultSet.getString("org_number")).thenReturn("111111111", "222222222");
when(mockResultSet.getString("charity_name")).thenReturn("Charity A", "Charity B");
when(mockResultSet.getString("charity_link")).thenReturn("https://a.org", "https://b.org");
when(mockResultSet.getBoolean("pre_approved")).thenReturn(true, false);
when(mockResultSet.getString("status")).thenReturn("ACTIVE", "INACTIVE");

when(mockResultSet.getString("UUID_Donations")).thenReturn("donation-uuid-1", "donation-uuid-2");
when(mockResultSet.getDouble("amount")).thenReturn(500.0, 750.0);

Date sqlDate = Date.valueOf(LocalDate.of(2024, 8, 10));
when(mockResultSet.getDate("date")).thenReturn(sqlDate);

DonationRegistry registry = donationSelect.getDonationFromDB();

assertEquals(2, registry.getDonations().size(),
"Registry should contain two donations for two result rows");
}

@Test
@DisplayName("getDonationFromDB – donation amount of zero is stored correctly")
void getDonationFromDB_zeroAmount_storedCorrectly() throws Exception {
when(mockDatabaseConnection.getMySqlConnection()).thenReturn(mockConnection);
when(mockConnection.createStatement()).thenReturn(mockStatement);
when(mockStatement.executeQuery(anyString())).thenReturn(mockResultSet);

when(mockResultSet.next()).thenReturn(true, false);
stubCharityColumns("charity-uuid-1", "123456789", "Test Charity",
"https://example.org", true, "ACTIVE");
stubDonationColumns("donation-uuid-zero", 0.0, LocalDate.of(2024, 1, 1));

DonationRegistry registry = donationSelect.getDonationFromDB();

assertEquals(0.0, registry.getDonations().get(0).getAmount());
}

@Test
@DisplayName("getDonationFromDB – large donation amount is stored correctly")
void getDonationFromDB_largeAmount_storedCorrectly() throws Exception {
when(mockDatabaseConnection.getMySqlConnection()).thenReturn(mockConnection);
when(mockConnection.createStatement()).thenReturn(mockStatement);
when(mockStatement.executeQuery(anyString())).thenReturn(mockResultSet);

when(mockResultSet.next()).thenReturn(true, false);
stubCharityColumns("charity-uuid-1", "123456789", "Test Charity",
"https://example.org", true, "ACTIVE");
stubDonationColumns("donation-uuid-big", 1_000_000.99, LocalDate.of(2024, 12, 31));

DonationRegistry registry = donationSelect.getDonationFromDB();

assertEquals(1_000_000.99, registry.getDonations().get(0).getAmount(), 0.001);
}

@Test
@DisplayName("getDonationFromDB – SQLException is wrapped in RuntimeException")
void getDonationFromDB_sqlException_throwsRuntimeException() throws Exception {
when(mockDatabaseConnection.getMySqlConnection()).thenReturn(mockConnection);
when(mockConnection.createStatement()).thenThrow(new SQLException("DB error"));

assertThrows(RuntimeException.class, () -> donationSelect.getDonationFromDB(),
"A SQLException should be rethrown as a RuntimeException");
}

@Test
@DisplayName("getDonationFromDB – RuntimeException message contains expected error text")
void getDonationFromDB_sqlException_runtimeExceptionHasExpectedMessage() throws Exception {
when(mockDatabaseConnection.getMySqlConnection()).thenReturn(mockConnection);
when(mockConnection.createStatement()).thenThrow(new SQLException("DB error"));

RuntimeException ex = assertThrows(RuntimeException.class,
() -> donationSelect.getDonationFromDB());
assertTrue(ex.getMessage().contains("ERROR"),
"RuntimeException message should contain 'ERROR'");
}

// -------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------

/**
* Stubs all charity-related columns on the mock ResultSet.
*/
private void stubCharityColumns(String uuid, String orgNumber, String name,
String link, boolean preApproved, String status)
throws SQLException {
when(mockResultSet.getString("UUID_charities")).thenReturn(uuid);
when(mockResultSet.getString("org_number")).thenReturn(orgNumber);
when(mockResultSet.getString("charity_name")).thenReturn(name);
when(mockResultSet.getString("charity_link")).thenReturn(link);
when(mockResultSet.getBoolean("pre_approved")).thenReturn(preApproved);
when(mockResultSet.getString("status")).thenReturn(status);
}

/**
* Stubs all donation-related columns on the mock ResultSet.
*/
private void stubDonationColumns(String uuid, double amount, LocalDate date)
throws SQLException {
when(mockResultSet.getString("UUID_Donations")).thenReturn(uuid);
when(mockResultSet.getDouble("amount")).thenReturn(amount);
Date sqlDate = Date.valueOf(date);
when(mockResultSet.getDate("date")).thenReturn(sqlDate);
}
}

0 comments on commit c1c1b7f

Please sign in to comment.