diff --git a/src/main/java/edu/group5/app/model/DBRepository.java b/src/main/java/edu/group5/app/model/DBRepository.java new file mode 100644 index 0000000..1196eae --- /dev/null +++ b/src/main/java/edu/group5/app/model/DBRepository.java @@ -0,0 +1,25 @@ +package edu.group5.app.model; + + +import java.util.HashMap; +import java.util.Map; + +/** + * Abstract base class for repositories that store their data + * in a database-related structure. + * + *

+ * Extends {@link Repository} and specifies that the content + * is stored as a {@link Map}. + *

+ */ +public abstract class DBRepository extends Repository { + /** + * Constructs a DBRepository with the given content. + * + * @param content the HashMap used to store repository entities + */ + protected DBRepository(Map content) { + super(content); + } +} diff --git a/src/main/java/edu/group5/app/model/Repository.java b/src/main/java/edu/group5/app/model/Repository.java index 6850bfe..5396b2f 100644 --- a/src/main/java/edu/group5/app/model/Repository.java +++ b/src/main/java/edu/group5/app/model/Repository.java @@ -3,11 +3,15 @@ import java.util.Map; /** - * Represents a repository + * Represents a repository. */ public abstract class Repository { protected final Map content; - + /** + * Constructs a new Repository with the specified content. + * + * @param content the underlying data structure used to store entities + */ public Repository(Map content) { this.content = content; } diff --git a/src/main/java/edu/group5/app/model/donation/Donation.java b/src/main/java/edu/group5/app/model/donation/Donation.java index a127439..92adfbe 100644 --- a/src/main/java/edu/group5/app/model/donation/Donation.java +++ b/src/main/java/edu/group5/app/model/donation/Donation.java @@ -1,5 +1,53 @@ package edu.group5.app.model.donation; -public class Donation { - +import java.math.BigDecimal; +import java.sql.Timestamp; + +/** + * Represents a verified donation made by a user to an organization. + * @param donationId - the unique ID of this donation + * @param userId - the ID of the user making the donation + * @param organizationId - the ID of the organization receiving the donation + * @param amount - the donation amount + * @param date - the timestamp when the donation was made + * @param paymentMethod - the payment method used + */ +public record Donation( + int donationId, + int userId, + int organizationId, + BigDecimal amount, + Timestamp date, + String paymentMethod) { + + /** + * Constructor with throws. + * + * @param donationId - throws if donationID is negative or 0. + * @param userId - throws if userID is negative or 0. + * @param organizationId - throws if organizationID is negative or 0. + * @param amount - throws if amount is negative or null. + * @param date - throws if date is null. + * @param paymentMethod - throws if payment is null or blank. + */ + public Donation { + if (donationId <= 0) { + throw new IllegalArgumentException("Donation ID must be positive"); + } + if (userId <= 0) { + throw new IllegalArgumentException("User ID must be positive"); + } + if (organizationId <= 0) { + throw new IllegalArgumentException("Organization ID must be positive"); + } + if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) { + throw new IllegalArgumentException("Amount must be positive and not null"); + } + if (date == null) { + throw new IllegalArgumentException("Date must not be null"); + } + if (paymentMethod == null || paymentMethod.isBlank()) { + throw new IllegalArgumentException("Payment method must not be empty"); + } + } } diff --git a/src/main/java/edu/group5/app/model/donation/DonationRepository.java b/src/main/java/edu/group5/app/model/donation/DonationRepository.java new file mode 100644 index 0000000..339e7ca --- /dev/null +++ b/src/main/java/edu/group5/app/model/donation/DonationRepository.java @@ -0,0 +1,119 @@ +package edu.group5.app.model.donation; + +import edu.group5.app.model.DBRepository; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Repository class for Donation. + * + *

+ * Extends {@link DBRepository} and manages Donation entities. + *

+ */ +public class DonationRepository extends DBRepository { + private final HashMap content; + + /** + * Constructs DonationRepository using Hashmap, + * and extends the content from DBRepository. + * @param content the underlying map used to store donations, + * where the key represents the donation ID + */ + public DonationRepository(HashMap content){ + if (content == null) { + throw new IllegalArgumentException("Content cannot be null"); + } + super(content); + this.content = content; + } + + /** + * Adds a new donation to the repository + *

+ * The donation is stored using its {@code donationId} as the key. + * If a donation with the same ID already exists, the donation + * will not be added. + *

+ * + * @param donation the donation to add + * @return {@code true} if the donation was successfully added, and + * {@code false} if a donation with the same ID already exists + */ + public boolean addDonation(Donation donation) { + if (donation == null) { + throw new IllegalArgumentException("Donation cannot be null"); + } + if (content.containsKey(donation.donationId())){ + return false; + } + content.put(donation.donationId(), donation); + return true; + } + + /** + * Returns all donations sorted by date (ascending). + * + *

+ * The returned map preserves the sorted order. + *

+ * + * @return a new {@link HashMap} containing the donations sorted by date + */ + public HashMap sortByDate(){ + return content.entrySet().stream() + .sorted(Map.Entry.comparingByValue( + Comparator.comparing(Donation::date))) + .collect(Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (e1, e2) -> e1, + LinkedHashMap::new)); + } + + /** + * Returns all donations sorted by amount (ascending). + * + *

+ * The returned map preserves the sorted order from lowest to highest amount. + *

+ * + * @return a new {@link HashMap} containing the donations sorted by amount. + */ + public HashMap sortByAmount() { + return content.entrySet().stream() + .sorted(Map.Entry.comparingByValue( + Comparator.comparing(Donation::amount))) + .collect(Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (e1, e2) -> e1, + LinkedHashMap::new + )); + } + + /** + * Returns all donations associated with a specific organization. + * + * @param orgNumber the organization ID to filter by + * @return a map containing all donations that belong to the given organization + */ + public HashMap filterByOrganization(int orgNumber) { + if (orgNumber <= 0) { + throw new IllegalArgumentException("Organization number must be positive"); + } + return content.entrySet() + .stream() + .filter(entry -> entry.getValue().organizationId() == orgNumber) + .collect(Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (e1, e2) -> e1, + LinkedHashMap::new + )); + } +} diff --git a/src/test/java/edu/group5/app/model/donation/DonationRepositoryTest.java b/src/test/java/edu/group5/app/model/donation/DonationRepositoryTest.java new file mode 100644 index 0000000..b55ef57 --- /dev/null +++ b/src/test/java/edu/group5/app/model/donation/DonationRepositoryTest.java @@ -0,0 +1,188 @@ +package edu.group5.app.model.donation; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.HashMap; + +import static org.junit.jupiter.api.Assertions.*; + +public class DonationRepositoryTest { + private DonationRepository repo; + private Timestamp now; + private Donation donation1; + private Donation donation2; + private Donation donation3; + private Donation donation4; + + @BeforeEach + void setUp() { + repo = new DonationRepository(new HashMap<>()); + + Timestamp ts1 = Timestamp.valueOf("2025-01-01 10:00:00"); + Timestamp ts2 = Timestamp.valueOf("2025-01-01 10:00:01"); + now = new Timestamp(System.currentTimeMillis()); + + donation1 = new Donation(1, 102, 202, new BigDecimal("500.0"), ts1, "Card"); + donation2 = new Donation(2, 102, 202, new BigDecimal("500.0"), ts2, "PayPal"); + donation3 = new Donation(3, 103, 203, new BigDecimal("200.0"), now, "PayPal"); + donation4 = new Donation(1, 101, 201, new BigDecimal("500.0"), now, "Card"); + + + } + @Test + void testThrowsExceptionIfContentIsNull() { + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, + () -> new DonationRepository(null)); + assertEquals("Content cannot be null", thrown.getMessage()); + } + + @Test + void testAddDonation() { + assertTrue(repo.addDonation(donation1)); + assertEquals(1, repo.getContent().size()); + + assertTrue(repo.addDonation(donation3)); + assertEquals(2, repo.getContent().size()); + } + @Test + void testAddDonationWithDuplicateId() { + assertTrue(repo.addDonation(donation1)); + assertFalse(repo.addDonation(donation4)); + assertEquals(1, repo.getContent().size()); + } + @Test + void testAddNullDonation() { + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, + () -> repo.addDonation(null)); + assertEquals("Donation cannot be null",thrown.getMessage()); + } + + @Test + void testSortByDate() { + repo.addDonation(donation3); + repo.addDonation(donation1); + repo.addDonation(donation2); + + HashMap sorted = repo.sortByDate(); + + Integer[] keys = sorted.keySet().toArray(new Integer[0]); + assertEquals(1, keys[0]); + assertEquals(2, keys[1]); + assertEquals(3, keys[2]); + + } + @Test + void testSortByDateWithSameDate() { + + repo.addDonation(donation3); + repo.addDonation(donation4); + + HashMap sorted = repo.sortByDate(); + + assertEquals(2, sorted.size()); + assertTrue(sorted.containsKey(1)); + assertTrue(sorted.containsKey(3)); + } + @Test + void testSortByDateWithSameDonation() { + Timestamp sameDate = Timestamp.valueOf("2025-01-01 10:00:00"); + + Donation d1 = new Donation(1, 101, 201, + new BigDecimal("500.0"), sameDate, "Card"); + + repo.addDonation(d1); + repo.addDonation(donation4); + + HashMap sorted = repo.sortByDate(); + + assertEquals(1, sorted.size()); + assertTrue(sorted.containsKey(1)); + assertFalse(sorted.containsKey(2)); + } + @Test + void TestSortByAmountTest() { + repo.addDonation(donation2); + repo.addDonation(donation3); + + HashMap sorted = repo.sortByAmount(); + Integer[] keys = sorted.keySet().toArray(new Integer[0]); + + assertEquals(3, keys[0]); + assertEquals(2, keys[1]); + + + } + @Test + void testSortByAmountWithSameAmount() { + repo.addDonation(donation1); + repo.addDonation(donation2); + + HashMap sorted = repo.sortByAmount(); + + assertEquals(2, sorted.size()); + assertTrue(sorted.containsKey(1)); + assertTrue(sorted.containsKey(2)); + } + @Test + void testSortByAmountWithSameDonation() { + + Donation d1 = new Donation(1, 101, 201, + new BigDecimal("500.0"), now, "Card"); + + repo.addDonation(d1); + repo.addDonation(donation4); + + HashMap sorted = repo.sortByAmount(); + + assertEquals(1, sorted.size()); + assertTrue(sorted.containsKey(1)); + assertFalse(sorted.containsKey(2)); + } + @Test + void testFilterByOrganization() { + repo.addDonation(donation1); + repo.addDonation(donation2); + + HashMap filtered = repo.filterByOrganization(202); + + assertEquals(2, filtered.size()); + assertTrue(filtered.containsKey(1)); + assertTrue(filtered.containsKey(2)); + assertFalse(filtered.containsKey(3)); + } + @Test + void testFilterByOrganizationNoMatch() { + repo.addDonation(donation1); + repo.addDonation(donation2); + + HashMap filtered = repo.filterByOrganization(999); + + assertTrue(filtered.isEmpty()); + } + @Test + void testThrowsExceptionWhenOrgNumberIsNegative() { + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, + () -> repo.filterByOrganization(-1)); + assertEquals("Organization number must be positive",thrown.getMessage()); + } + @Test + void testFilterByOrganizationWithSameDonation() { + Timestamp sameDate = Timestamp.valueOf("2025-01-01 10:00:00"); + + Donation d1 = new Donation(1, 101, 201, + new BigDecimal("500.0"), sameDate, "Card"); + + repo.addDonation(d1); + repo.addDonation(donation4); + + HashMap sorted = repo.filterByOrganization(201); + + assertEquals(1, sorted.size()); + assertTrue(sorted.containsKey(1)); + assertFalse(sorted.containsKey(2)); + } + +} diff --git a/src/test/java/edu/group5/app/model/donation/DonationTest.java b/src/test/java/edu/group5/app/model/donation/DonationTest.java index a905209..f8f9069 100644 --- a/src/test/java/edu/group5/app/model/donation/DonationTest.java +++ b/src/test/java/edu/group5/app/model/donation/DonationTest.java @@ -1,5 +1,99 @@ package edu.group5.app.model.donation; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.sql.Timestamp; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + public class DonationTest { - + + private String expectedMessage; + private int donationId1; + private int userId1; + private int organizationId1; + private BigDecimal amount1; + private Timestamp date1; + private String paymentMethod1; + private Donation donation; + + @BeforeEach + void setUp(){ + donationId1 = 1; + userId1 = 101; + organizationId1 = 202; + amount1 = new BigDecimal("500.0"); + date1 = new Timestamp(System.currentTimeMillis()); + paymentMethod1 = "Card"; + donation = new Donation(donationId1, userId1, + organizationId1, amount1, date1, paymentMethod1); + + } + + private void exceptionTest(int donationId, int userId, + int organizationId, BigDecimal amount, + Timestamp date, String paymentMethod) { + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, () -> new Donation( + donationId, userId, organizationId, amount, date, paymentMethod) + ); + assertEquals(expectedMessage, exception.getMessage()); + } + + @Test + void testIfDonationGetsDonationValues() { + + assertEquals(1, donation.donationId()); + assertEquals(101, donation.userId()); + assertEquals(202, donation.organizationId()); + assertEquals(new BigDecimal("500.0"), donation.amount()); + assertEquals(date1, donation.date()); + assertEquals("Card", donation.paymentMethod()); + } + + @Test + void testIfThrowsExceptionWhenDonationIdIsNotPositive() { + expectedMessage = "Donation ID must be positive"; + exceptionTest(0, userId1, organizationId1, + amount1, date1, paymentMethod1); + } + + @Test + void testIfThrowsExceptionWhenUserIdIsNotPositive() { + expectedMessage = "User ID must be positive"; + exceptionTest(donationId1, -1, organizationId1, + amount1, date1, paymentMethod1); + } + @Test + void testIfThrowsExceptionWhenOrganizationIdIsNotPositive() { + expectedMessage = "Organization ID must be positive"; + exceptionTest(donationId1, userId1, 0, + amount1, date1, paymentMethod1); + } + @Test + void testIfThrowsExceptionWhenAmountIsNotPositive() { + expectedMessage = "Amount must be positive and not null"; + exceptionTest(donationId1, userId1, organizationId1, + new BigDecimal("0.00"), date1, paymentMethod1); + } + @Test + void testIfThrowsExceptionWhenDateIsNull() { + expectedMessage = "Date must not be null"; + exceptionTest(donationId1, userId1, organizationId1, + amount1, null, paymentMethod1); + } + @Test + void testIfThrowsExceptionWhenPaymentMethodIsNullOrEmpty() { + expectedMessage = "Payment method must not be empty"; + exceptionTest(donationId1, userId1, organizationId1, + amount1, date1, null); + exceptionTest(donationId1, userId1, organizationId1, + amount1, date1, " "); + + } + + }