diff --git a/pom.xml b/pom.xml index 6320c10..7a55551 100644 --- a/pom.xml +++ b/pom.xml @@ -31,6 +31,13 @@ org.openjfx javafx-controls ${javafx.version} + win + + + org.openjfx + javafx-graphics + ${javafx.version} + win @@ -38,11 +45,6 @@ spring-security-crypto 7.0.2 - - commons-logging - commons-logging - 1.3.5 - tools.jackson.core @@ -50,6 +52,16 @@ 3.1.0 compile + + org.springframework + spring-core + 6.1.10 + + + org.slf4j + slf4j-simple + 2.0.9 + @@ -111,6 +123,19 @@ shade + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + META-INF/NOTICE + META-INF/LICENSE + META-INF.versions.9.module-info + + + edu.group5.app.App diff --git a/src/main/java/edu/group5/app/model/DBRepository.java b/src/main/java/edu/group5/app/model/DBRepository.java index 9dac3c0..799a977 100644 --- a/src/main/java/edu/group5/app/model/DBRepository.java +++ b/src/main/java/edu/group5/app/model/DBRepository.java @@ -1,5 +1,7 @@ package edu.group5.app.model; +import java.util.HashMap; import java.util.Map; +import java.util.List; /** * Abstract base class for repositories that store their data * in a database-related structure. @@ -10,6 +12,7 @@ *

*/ public abstract class DBRepository extends Repository { + protected final Map contentLock; /** * Constructs a DBRepository with the given content. * @@ -17,5 +20,26 @@ public abstract class DBRepository extends Repository { */ protected DBRepository(Map content) { super(content); + this.contentLock = new HashMap<>(); } + + protected void updateContentLock() { + synchronized (contentLock) { + contentLock.clear(); + contentLock.putAll(this.content); + } + } + + public void addContent(K key, V value) { + synchronized (contentLock) { + content.put(key, value); + } + } + + /** + * Exports the repository content as a list of Object arrays, where each array represents a row of data. + * This method is intended for converting the repository content into a format suitable for database storage or export. + * @return a List of Object arrays representing the repository content for database export + */ + public abstract List export(); } diff --git a/src/main/java/edu/group5/app/model/donation/DonationRepository.java b/src/main/java/edu/group5/app/model/donation/DonationRepository.java index 339e7ca..54845a5 100644 --- a/src/main/java/edu/group5/app/model/donation/DonationRepository.java +++ b/src/main/java/edu/group5/app/model/donation/DonationRepository.java @@ -7,6 +7,9 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.stream.Collectors; +import java.util.List; +import java.math.BigDecimal; +import java.sql.Timestamp; /** * Repository class for Donation. @@ -19,17 +22,70 @@ 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 + * Constructs DonationRepository from a list of Object[] rows from the database. + * @param rows List of Object[] representing donations from the DB. + * Each row must have 6 elements: + * [donationId, userId, organizationId, amount, date, paymentMethod] + * @throws IllegalArgumentException if the input list is null or any row is invalid */ - public DonationRepository(HashMap content){ - if (content == null) { - throw new IllegalArgumentException("Content cannot be null"); + public DonationRepository(List rows) { + super(new HashMap<>()); + if (rows == null) { + throw new IllegalArgumentException("The list of rows cannot be null"); } - super(content); - this.content = content; + this.content = new HashMap<>(); + for (Object[] row : rows) { + if (row == null || row.length != 6 ) { + throw new IllegalArgumentException("Each row must contain exactly 6 elements"); + } + int donationId = (int) row[0]; + int customerId = (int) row[1]; + int organizationId = (int) row[2]; + BigDecimal amount = (BigDecimal) row[3]; + Timestamp date = (Timestamp) row[4]; + String paymentMethod = (String) row[5]; + + Donation donation = new Donation(donationId, customerId, organizationId, amount, date, paymentMethod); + this.content.put(donationId, donation); + } + super.updateContentLock(); + } + + @Override + public List export() { + return content.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .map(entry -> { Donation donation = entry.getValue(); + return new Object[] { + donation.donationId(), donation.userId(), + donation.organizationId(), donation.amount(), + donation.date(), donation.paymentMethod()};}) + .toList(); + } + + /** + * Retrieves a donation by its ID. + * @param donationId the ID of the donation to retrieve + * @return the Donation object with the specified ID, or null if not found + * @throws IllegalArgumentException if the donationId is not positive + */ + public Donation getDonationById(int donationId) { + if (donationId <= 0) { + throw new IllegalArgumentException("Donation ID must be positive"); + } + return content.get(donationId); + } + + /** + * Generates the next donation ID based on the current maximum ID in the repository. + * @return the next donation ID to be used for a new donation + */ + public int getNextDonationId() { + return content.keySet().stream().max(Integer::compareTo).orElse(0) + 1; + } /* TODO change this when data database is introduced */ + + public Map getAllDonations() { + return new HashMap<>(content); } /** diff --git a/src/main/java/edu/group5/app/model/donation/DonationService.java b/src/main/java/edu/group5/app/model/donation/DonationService.java index 11dde6c..d8fef60 100644 --- a/src/main/java/edu/group5/app/model/donation/DonationService.java +++ b/src/main/java/edu/group5/app/model/donation/DonationService.java @@ -46,15 +46,17 @@ public DonationService(DonationRepository donationRepository, * @return true if the donation is successfully processed, false otherwise */ public boolean donate(Customer customer, int orgNumber, BigDecimal amount, String paymentMethod) { - if (customer == null || amount == null || amount.compareTo(BigDecimal.ZERO) <= 0 || paymentMethod.isBlank()) { + if (customer == null || amount == null + || amount.compareTo(BigDecimal.ZERO) <= 0 || paymentMethod.isBlank()) { return false; } Organization org = organizationRepository.findByOrgNumber(orgNumber); if (org == null) { return false; } - Donation donation = new Donation(1, customer.getUserId(), org.orgNumber(), /* TODO fix incrementation of donation */ - amount, Timestamp.from(Instant.now()), paymentMethod); + Donation donation = + new Donation(donationRepository.getNextDonationId(), + customer.getUserId(), org.orgNumber(), amount, Timestamp.from(Instant.now()), paymentMethod); donationRepository.addDonation(donation); return true; } diff --git a/src/main/java/edu/group5/app/model/organization/OrganizationRepository.java b/src/main/java/edu/group5/app/model/organization/OrganizationRepository.java index 53b4241..4499288 100644 --- a/src/main/java/edu/group5/app/model/organization/OrganizationRepository.java +++ b/src/main/java/edu/group5/app/model/organization/OrganizationRepository.java @@ -8,11 +8,22 @@ import java.util.Map; /** + * Repository class for managing Organization entities. It provides methods to retrieve trusted organizations, + * find organizations by their organization number or name, and initializes the repository with input data. + * The repository uses a HashMap to store Organization objects for efficient retrieval based on their organization number. * Handles the business logic associated with organizations */ public class OrganizationRepository extends Repository { private final HashMap grandMap; + /** + * Initializes the repository with the given input data, c + * onverting it into Organization objects and storing them in a map for efficient retrieval. + * The input is expected to be an array of objects, where each object contains + * the necessary information to create an Organization. + * @param input the input data used to populate the repository, must not be null + * @throws IllegalArgumentException if the input is null + */ public OrganizationRepository(Object[] input) { super(new HashMap<>()); grandMap = new HashMap<>(); @@ -27,23 +38,38 @@ public OrganizationRepository(Object[] input) { String orgNumberStr = ((String) contentMap.get("org_number")).replaceAll("\\s", ""); int orgNumber = Integer.parseInt(orgNumberStr); - String name = (String) contentMap.get("name"); - boolean trusted = "approved".equalsIgnoreCase((String) contentMap.get("status")); - String websiteURL = (String) contentMap.get("url"); - boolean isPreApproved = Boolean.TRUE.equals(contentMap.get("is_pre_approved")); - String description = "Information about " + name; - Organization org = new Organization(orgNumber, name, trusted, websiteURL, isPreApproved, description); grandMap.put(org.orgNumber(), org); } } + /** + * Exports the organization data in a format suitable for output, such as JSON. Each organization is represented as a map containing its attributes. + * @return an array of maps, where each map represents an organization with its attributes (org_number, name, status, url, is_pre_approved) + * @throws IllegalStateException if the repository is empty + */ + public Object[] export() { + if (grandMap.isEmpty()) { + throw new IllegalStateException("The repository is empty"); + } + return grandMap.values().stream() + .map(org -> { Map orgMap = new HashMap<>(); + orgMap.put("org_number", org.orgNumber()); + orgMap.put("name", org.name()); + orgMap.put("status", org.trusted() ? "approved" : "unapproved"); + orgMap.put("url", org.websiteURL()); + orgMap.put("is_pre_approved", org.isPreApproved()); + return orgMap; + }) + .toArray(); + } + /** * Gets all trusted organizations in the repository * @return all organizations with trusted = true @@ -59,11 +85,32 @@ public Map getTrustedOrganizations() { return trustedOrganizations; } + /** + * Finds an organization by its organization number + * @param orgNumber the organization number of the Organization + * @return the Organization with the given organization number, or null if not found + * @throws IllegalArgumentException if the organization number is not a positive integer + */ public Organization findByOrgNumber(int orgNumber) { - if (orgNumber == 0 || orgNumber <= 0) { + if (orgNumber <= 0) { throw new IllegalArgumentException("The Organization number must be a positive integer"); } return grandMap.get(orgNumber); } + /** + * Finds an organization by its name, ignoring case + * @param name the name of the Organization + * @return the Organization with the given name, or null if not found + * @throws IllegalArgumentException if the name is null or empty + */ + public Organization findByOrgName(String name) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("The name cannot be null"); + } + return grandMap.values().stream() + .filter(org -> org.name().equalsIgnoreCase(name)) + .findFirst() + .orElse(null); + } } diff --git a/src/main/java/edu/group5/app/model/user/Customer.java b/src/main/java/edu/group5/app/model/user/Customer.java index 5d3fa7e..93f03ac 100644 --- a/src/main/java/edu/group5/app/model/user/Customer.java +++ b/src/main/java/edu/group5/app/model/user/Customer.java @@ -11,7 +11,6 @@ */ public class Customer extends User { private List preferences; - /** * Constructs a new Customer object for new registrations. * The role is automatically set to "Customer" and the preferences list is initialized empty. @@ -25,27 +24,7 @@ public class Customer extends User { */ public Customer(int userId, String firstName, String lastName, String email, String passwordHash) { - super(userId, "Customer", firstName, lastName, email, passwordHash); - this.preferences = new ArrayList<>(); -} - -/** - * Constructs a Customer object for existing users loaded from the database - * or for cases where the role is already defined. - * The role should normally be "Customer", but this constructor allows flexibility - * (e.g., for testing or future user types). Preferences list is initialized empty. - * - * @param userId the unique identifier for the user, must be positive - * @param role the role of the user (should be "Customer" for customer instances) - * @param firstName the first name of the customer - * @param lastName the last name of the customer - * @param email the email address of the customer - * @param passwordHash the hashed password of the customer - * @throws IllegalArgumentException if any parameter is invalid (null, empty, or userId ≤ 0) - */ -public Customer(int userId, String role, String firstName, String lastName, - String email, String passwordHash) { - super(userId, role, firstName, lastName, email, passwordHash); + super(userId, firstName, lastName, email, passwordHash); this.preferences = new ArrayList<>(); } @@ -53,6 +32,11 @@ public List getPreferences() { return preferences; } + @Override + public String getRole() { + return UserRepository.ROLE_CUSTOMER; + } + public void addPreference(int orgNumber) { if (orgNumber <= 0) { throw new IllegalArgumentException("Organization number must be a positive integer"); diff --git a/src/main/java/edu/group5/app/model/user/User.java b/src/main/java/edu/group5/app/model/user/User.java index e20e4f4..bdb4700 100644 --- a/src/main/java/edu/group5/app/model/user/User.java +++ b/src/main/java/edu/group5/app/model/user/User.java @@ -11,7 +11,6 @@ */ public abstract class User { private int userId; - private String role; private String firstName; private String lastName; private String email; @@ -21,20 +20,16 @@ public abstract class User { * Constructor for User class. Validates that all required fields * are provided and throws an IllegalArgumentException if any of the fields are null or empty. * @param userId the unique identifier for the user, must be a positive integer - * @param role the role of the user (e.g., "Donor", "Recipient", "Admin") * @param firstName the first name of the user * @param lastName the last name of the user * @param email the email address of the user * @param passwordHash the hashed password of the user, used for authentication purposes */ - public User(int userId, String role, String firstName, + public User(int userId, String firstName, String lastName, String email, String passwordHash) { if (userId <= 0) { throw new IllegalArgumentException("User ID must be positive"); } - if (role == null || role.trim().isEmpty()) { - throw new IllegalArgumentException("Role cannot be null or empty"); - } if (firstName == null || firstName.trim().isEmpty()) { throw new IllegalArgumentException("First name cannot be null or empty"); } @@ -48,7 +43,6 @@ public User(int userId, String role, String firstName, throw new IllegalArgumentException("Password hash cannot be null or empty"); } this.userId = userId; - this.role = role.trim(); this.firstName = firstName.trim(); this.lastName = lastName.trim(); this.email = email.trim(); @@ -62,14 +56,13 @@ public User(int userId, String role, String firstName, public int getUserId() { return userId; } - + /** - * Gets the role of the user, which defines their permissions in the system. + * Gets the role of the user (e.g., "Customer", "Admin"). * @return the role of the user */ - public String getRole() { - return role; - } + public abstract String getRole(); + /** * Gets the first name of the user. diff --git a/src/main/java/edu/group5/app/model/user/UserRepository.java b/src/main/java/edu/group5/app/model/user/UserRepository.java index 18bbd09..8ccb790 100644 --- a/src/main/java/edu/group5/app/model/user/UserRepository.java +++ b/src/main/java/edu/group5/app/model/user/UserRepository.java @@ -1,39 +1,106 @@ package edu.group5.app.model.user; import java.util.HashMap; +import java.util.List; +import java.util.Map; import edu.group5.app.model.DBRepository; public class UserRepository extends DBRepository{ - + public final static String ROLE_CUSTOMER = "Customer"; /** - * Constructs DonationRepository using Hashmap, + * Constructs UserRepository using Hashmap, * and extends the content from DBRepository. - * @param content the underlying map used to store donations, - * where the key represents the donation ID + * @param content the underlying map used to store users, + * where the key represents the user ID */ - public UserRepository(HashMap content) { - super(content); - if (content == null) { - throw new IllegalArgumentException("Content cannot be null"); + public UserRepository(List rows) { + super(new HashMap<>()); + if (rows == null) { + throw new IllegalArgumentException("The list of rows cannot be null"); + } + for (Object[] row : rows) { + if (row == null || row.length != 6 ) { + throw new IllegalArgumentException("Each row must contain exactly 6 elements"); + } + int userId = (int) row[0]; + String role = (String) row[1]; + String firstName = (String) row[2]; + String lastName = (String) row[3]; + String email = (String) row[4]; + String passwordHash = (String) row[5]; + + User user; + if (ROLE_CUSTOMER.equalsIgnoreCase(role)) { + user = new Customer(userId, firstName, lastName, email, passwordHash); + } else { + throw new IllegalArgumentException("Unknown role: " + role); + } + this.content.put(userId, user); } + super.updateContentLock(); + } + + + @Override + public List export() { + return content.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .map(entry -> { User user = entry.getValue(); + return new Object[]{user.getUserId(), user.getRole(), + user.getFirstName(), user.getLastName(), + user.getEmail(), user.getPasswordHash()};}) + .toList(); } + public HashMap getUsers() { + return new HashMap<>(content); + } /** - * Adds a new donation to the repository + * Retrieves a user by their unique identifier. + * @param userId the unique identifier of the user to retrieve + * @return the user with the specified ID, or {@code null} if no such user exists + * @throws IllegalArgumentException if the userId is not positive + */ + public User getUserById(int userId) { + if (userId <= 0) { + throw new IllegalArgumentException("User ID must be positive"); + } + return content.get(userId); + } + + /** + * Generates the next user ID based on repository size. + * Uses size+1 and then moves forward if that ID is already taken. + * @return the next available user ID + * @throws IllegalStateException if no available user ID can be found + */ + public int getNextUserId() { + int nextId = content.size() + 1; + while (content.containsKey(nextId)) { + nextId++; + if (nextId <= 0) { + throw new IllegalStateException("No available user ID found"); + } + } + return nextId; + } /* TODO change this when data database is introduced */ + + /** + * Adds a new user 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 + * The user is stored using its {@code userId} as the key. + * If a user with the same ID already exists, the user * 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 + * @param user the user to add + * @return {@code true} if the user was successfully added, and + * {@code false} if a user with the same ID already exists */ public boolean addUser(User user) { if (user == null) { - throw new IllegalArgumentException("Donation cannot be null"); + throw new IllegalArgumentException("User cannot be null"); } if (content.containsKey(user.getUserId())){ return false; @@ -42,7 +109,18 @@ public boolean addUser(User user) { return true; } + /** + * Finds a user by their email address. + * @param email the email address of the user to find + * @return the user with the specified email address, or {@code null} if no such user exists + */ public User findUserByEmail(String email) { - return null; + if (email == null || email.trim().isEmpty()) { + throw new IllegalArgumentException("Email cannot be null or empty"); + } + return content.values().stream() + .filter(user -> user.getEmail().equals(email)) + .findFirst() + .orElse(null); } } diff --git a/src/main/java/edu/group5/app/model/user/UserService.java b/src/main/java/edu/group5/app/model/user/UserService.java index d75d380..7a9dc53 100644 --- a/src/main/java/edu/group5/app/model/user/UserService.java +++ b/src/main/java/edu/group5/app/model/user/UserService.java @@ -1,9 +1,18 @@ package edu.group5.app.model.user; +/** + * Service class for managing user-related operations, such as registration and login. + * It interacts with the UserRepository to perform these operations and contains the business logic + * associated with user management, including validation of input data and handling of user authentication. + */ public class UserService { private UserRepository userRepository; - + /** + * Constructs a UserService with the given UserRepository. + * @param userRepository the UserRepository to use for managing user data; must not be null + * @throws IllegalArgumentException if userRepository is null + */ public UserService(UserRepository userRepository) { if (userRepository == null) { throw new IllegalArgumentException("UserRepository cannot be null"); @@ -11,6 +20,19 @@ public UserService(UserRepository userRepository) { this.userRepository = userRepository; } + /** + * Registers a new user with the given information. Validates the input data and creates a new User object + * based on the specified role. Currently supports registration for customers only. + * @param role the role of the user (e.g., "Customer"); must not be null or empty + * @param firstName the first name of the user; must not be null or empty + * @param lastName the last name of the user; must not be null or empty + * @param email the email address of the user; must not be null or empty + * @param passwordHash the hashed password of the user; must not be null or empty + * @return true if the user was successfully registered, false if any input is invalid or + * if the role is not supported + * @throws IllegalArgumentException if any of the input parameters are null or empty + * or if the role is not supported + */ public boolean registerUser(String role, String firstName, String lastName, String email, String passwordHash) { if (role == null || role.trim().isEmpty() || @@ -22,14 +44,23 @@ public boolean registerUser(String role, String firstName, String lastName, } User user; if (role.equalsIgnoreCase("Customer")) { - user = new Customer(0, firstName, lastName, email, passwordHash); - } else { + user = new Customer(userRepository.getNextUserId(), firstName, lastName, email, passwordHash); + } else { /* TODO when you switch to a real DB, + replace getNextUserId with DB auto-increment/identity and ignore manual ID generation in service*/ return false; } userRepository.addUser(user); return true; } + /** + * Authenticates a user based on the provided email and password. + * @param email the email address of the user attempting to log in; must not be null or empty + * @param password the plaintext password of the user attempting to log in; must not be + * @return true if the login is successful + * (i.e., the user exists and the password is correct), false otherwise + * @throws IllegalArgumentException if email is null or empty, or if password is null + */ public boolean login(String email, String password) { if (email == null || email.trim().isEmpty() || password == null) { return false; diff --git a/src/test/java/edu/group5/app/model/donation/DonationRepositoryTest.java b/src/test/java/edu/group5/app/model/donation/DonationRepositoryTest.java index b55ef57..04d4626 100644 --- a/src/test/java/edu/group5/app/model/donation/DonationRepositoryTest.java +++ b/src/test/java/edu/group5/app/model/donation/DonationRepositoryTest.java @@ -5,24 +5,20 @@ import java.math.BigDecimal; import java.sql.Timestamp; -import java.util.HashMap; +import java.util.*; 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; + private Donation donation1, donation2, donation3, donation4; + private Timestamp ts1, ts2, now; @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"); + ts1 = Timestamp.valueOf("2025-01-01 10:00:00"); + 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"); @@ -30,159 +26,217 @@ void setUp() { donation3 = new Donation(3, 103, 203, new BigDecimal("200.0"), now, "PayPal"); donation4 = new Donation(1, 101, 201, new BigDecimal("500.0"), now, "Card"); + repo = new DonationRepository(new ArrayList<>()); + } + @Test + void constructorThrowsIfNullList() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> new DonationRepository(null)); + assertEquals("The list of rows cannot be null", ex.getMessage()); } + @Test - void testThrowsExceptionIfContentIsNull() { - IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, - () -> new DonationRepository(null)); - assertEquals("Content cannot be null", thrown.getMessage()); + void constructorThrowsIfRowInvalidLength() { + List invalidRows = new ArrayList<>(); + invalidRows.add(new Object[]{1, 2}); + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> new DonationRepository(invalidRows)); + assertEquals("Each row must contain exactly 6 elements", ex.getMessage()); } @Test - void testAddDonation() { - assertTrue(repo.addDonation(donation1)); - assertEquals(1, repo.getContent().size()); + void constructorParsesRowSuccessfully() { + List rows = new ArrayList<>(); + Timestamp ts = Timestamp.valueOf("2025-01-01 10:00:00"); + rows.add(new Object[]{1, 101, 201, new BigDecimal("100"), ts, "Card"}); + DonationRepository repoWithRow = new DonationRepository(rows); + Donation d = repoWithRow.getDonationById(1); + assertNotNull(d); + assertEquals(101, d.userId()); + assertEquals(201, d.organizationId()); + assertEquals(new BigDecimal("100"), d.amount()); + assertEquals(ts, d.date()); + assertEquals("Card", d.paymentMethod()); + } + - assertTrue(repo.addDonation(donation3)); - assertEquals(2, repo.getContent().size()); + @Test + void constructorThrowsIfRowIsNull() { + List rows = new ArrayList<>(); + rows.add(null); + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> new DonationRepository(rows)); + assertEquals("Each row must contain exactly 6 elements", ex.getMessage()); } + @Test - void testAddDonationWithDuplicateId() { + void addDonationSuccessfully() { assertTrue(repo.addDonation(donation1)); + assertEquals(1, repo.getAllDonations().size()); + assertTrue(repo.getAllDonations().containsValue(donation1)); + } + + @Test + void addDonationDuplicateIdFails() { + repo.addDonation(donation1); assertFalse(repo.addDonation(donation4)); - assertEquals(1, repo.getContent().size()); + assertEquals(1, repo.getAllDonations().size()); } + @Test - void testAddNullDonation() { - IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, - () -> repo.addDonation(null)); - assertEquals("Donation cannot be null",thrown.getMessage()); + void addDonationNullThrows() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> repo.addDonation(null)); + assertEquals("Donation cannot be null", ex.getMessage()); } @Test - void testSortByDate() { - repo.addDonation(donation3); + void getDonationByIdSuccessfully() { repo.addDonation(donation1); - repo.addDonation(donation2); - - HashMap sorted = repo.sortByDate(); + Donation retrieved = repo.getDonationById(1); + assertEquals(donation1, retrieved); + } - Integer[] keys = sorted.keySet().toArray(new Integer[0]); - assertEquals(1, keys[0]); - assertEquals(2, keys[1]); - assertEquals(3, keys[2]); + @Test + void getDonationByIdThrowsIfNegative() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> repo.getDonationById(0)); + assertEquals("Donation ID must be positive", ex.getMessage()); + } + @Test + void getDonationByIdReturnsNullIfNotFound() { + assertNull(repo.getDonationById(999)); } + @Test - void testSortByDateWithSameDate() { + void getAllDonationsReturnsCopy() { + repo.addDonation(donation1); + Map all = repo.getAllDonations(); + assertEquals(1, all.size()); + all.clear(); + assertEquals(1, repo.getAllDonations().size()); + } + @Test + void sortByDateAscending() { repo.addDonation(donation3); - repo.addDonation(donation4); - - HashMap sorted = repo.sortByDate(); + repo.addDonation(donation1); + repo.addDonation(donation2); - assertEquals(2, sorted.size()); - assertTrue(sorted.containsKey(1)); - assertTrue(sorted.containsKey(3)); + Map sorted = repo.sortByDate(); + Integer[] keys = sorted.keySet().toArray(new Integer[0]); + assertArrayEquals(new Integer[]{1, 2, 3}, keys); } - @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); + @Test + void sortByDateWithSameDateKeepsAll() { + repo.addDonation(donation1); repo.addDonation(donation4); - - HashMap sorted = repo.sortByDate(); - + Map sorted = repo.sortByDate(); assertEquals(1, sorted.size()); assertTrue(sorted.containsKey(1)); - assertFalse(sorted.containsKey(2)); } + @Test - void TestSortByAmountTest() { - repo.addDonation(donation2); + void sortByAmountAscending() { repo.addDonation(donation3); + repo.addDonation(donation1); - HashMap sorted = repo.sortByAmount(); + Map sorted = repo.sortByAmount(); Integer[] keys = sorted.keySet().toArray(new Integer[0]); - - assertEquals(3, keys[0]); - assertEquals(2, keys[1]); - - + assertArrayEquals(new Integer[]{3, 1}, keys); } + @Test - void testSortByAmountWithSameAmount() { + void sortByAmountWithSameAmount() { repo.addDonation(donation1); repo.addDonation(donation2); - - HashMap sorted = repo.sortByAmount(); - + Map 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() { + void filterByOrganizationMatches() { repo.addDonation(donation1); repo.addDonation(donation2); + repo.addDonation(donation3); - HashMap filtered = repo.filterByOrganization(202); - + Map filtered = repo.filterByOrganization(202); assertEquals(2, filtered.size()); assertTrue(filtered.containsKey(1)); assertTrue(filtered.containsKey(2)); - assertFalse(filtered.containsKey(3)); } + @Test - void testFilterByOrganizationNoMatch() { + void filterByOrganizationNoMatch() { repo.addDonation(donation1); - repo.addDonation(donation2); - - HashMap filtered = repo.filterByOrganization(999); - + Map 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()); + void filterByOrganizationThrowsIfNegative() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> repo.filterByOrganization(0)); + assertEquals("Organization number must be positive", ex.getMessage()); } + @Test - void testFilterByOrganizationWithSameDonation() { - Timestamp sameDate = Timestamp.valueOf("2025-01-01 10:00:00"); + void exportReturnsAllRowsSortedById() { + repo.addDonation(donation2); + repo.addDonation(donation1); - Donation d1 = new Donation(1, 101, 201, - new BigDecimal("500.0"), sameDate, "Card"); + List exported = repo.export(); + assertEquals(2, exported.size()); + assertArrayEquals(new Object[]{1, 102, 202, new BigDecimal("500.0"), ts1, "Card"}, exported.get(0)); + assertArrayEquals(new Object[]{2, 102, 202, new BigDecimal("500.0"), ts2, "PayPal"}, exported.get(1)); + } - repo.addDonation(d1); + @Test + void getNextDonationIdReturnsCorrectNextId() { + assertEquals(1, repo.getNextDonationId()); + repo.addDonation(donation1); repo.addDonation(donation4); + assertEquals(2, repo.getNextDonationId()); + } - HashMap sorted = repo.filterByOrganization(201); + @Test + void exportEmptyRepositoryReturnsEmptyList() { + List exported = repo.export(); + assertTrue(exported.isEmpty()); + } - assertEquals(1, sorted.size()); - assertTrue(sorted.containsKey(1)); - assertFalse(sorted.containsKey(2)); + @Test + void sortByDateHandlesDuplicateKeysWithLambda() { + Donation d1 = new Donation(1, 101, 201, new BigDecimal("100"), ts1, "Card"); + Donation d2 = new Donation(1, 102, 202, new BigDecimal("200"), ts2, "PayPal"); + repo.addDonation(d1); + repo.getAllDonations().put(d2.donationId(), d2); + Map sorted = repo.sortByDate(); + assertEquals(d1, sorted.get(1)); + } + + @Test + void sortByAmountHandlesDuplicateKeysWithLambda() { + Donation d1 = new Donation(1, 101, 201, new BigDecimal("100"), ts1, "Card"); + Donation d2 = new Donation(1, 102, 202, new BigDecimal("100"), ts2, "PayPal"); + repo.addDonation(d1); + repo.getAllDonations().put(d2.donationId(), d2); + Map sorted = repo.sortByAmount(); + assertEquals(d1, sorted.get(1)); } -} + @Test + void filterByOrganizationHandlesDuplicateKeysWithLambda() { + Donation d1 = new Donation(1, 101, 201, new BigDecimal("100"), ts1, "Card"); + Donation d2 = new Donation(1, 102, 201, new BigDecimal("200"), ts2, "PayPal"); + repo.addDonation(d1); + repo.getAllDonations().put(d2.donationId(), d2); + Map filtered = repo.filterByOrganization(201); + assertEquals(d1, filtered.get(1)); + } +} \ No newline at end of file diff --git a/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java b/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java new file mode 100644 index 0000000..5117373 --- /dev/null +++ b/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java @@ -0,0 +1,116 @@ +package edu.group5.app.model.donation; + +import edu.group5.app.model.organization.Organization; +import edu.group5.app.model.organization.OrganizationRepository; +import edu.group5.app.model.user.Customer; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class DonationServiceTest { + + private DonationRepository donationRepository; + private OrganizationRepository organizationRepository; + private DonationService donationService; + private Customer customer; + + @BeforeEach + void setUp() { + HashMap orgMap = new HashMap<>(); + orgMap.put("org_number", "101"); + orgMap.put("name", "CharityOrg"); + orgMap.put("status", "approved"); + orgMap.put("url", "https://charity.org"); + orgMap.put("is_pre_approved", true); + + Object[] orgInput = new Object[]{ orgMap }; + organizationRepository = new OrganizationRepository(orgInput); + + donationRepository = new DonationRepository(new ArrayList<>()); + + + donationService = new DonationService(donationRepository, organizationRepository); + + customer = new Customer(1, "John", "Doe", "john@example.com", "$2a$10$hashed"); + } + + @Test + void testConstructorThrowsIfDonationRepositoryIsNull() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { + new DonationService(null, organizationRepository); + }); + assertEquals("DonationRepository cannot be null", exception.getMessage()); + } + + @Test + void testConstructorThrowsIfOrganizationRepositoryIsNull() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { + new DonationService(donationRepository, null); + }); + assertEquals("OrganizationRepository cannot be null", exception.getMessage()); + } + + @Test + void donateReturnsFalseIfCustomerNull() { + boolean result = donationService.donate(null, + 101, BigDecimal.TEN, "Card"); + assertFalse(result); + } + + @Test + void donateReturnsFalseIfAmountInvalid() { + assertFalse(donationService.donate(customer, + 101, null, "Card")); + assertFalse(donationService.donate(customer, + 101, BigDecimal.ZERO, "Card")); + assertFalse(donationService.donate(customer, + 101, new BigDecimal("-5"), "Card")); + } + + @Test + void donateReturnsFalseIfPaymentMethodBlank() { + assertFalse(donationService.donate(customer, + 101, BigDecimal.TEN, "")); + assertFalse(donationService.donate(customer, + 101, BigDecimal.TEN, " ")); + } + + @Test + void donateReturnsFalseIfOrganizationNotFound() { + boolean result = donationService.donate(customer, 999, BigDecimal.TEN, "Card"); + assertFalse(result); + } + + @Test + void testDonateAddsDonationSuccessfully() { + BigDecimal amount = new BigDecimal("50.00"); + String paymentMethod = "PayPal"; + boolean result = donationService.donate(customer, 101, amount, paymentMethod); + assertTrue(result); + assertEquals(1, donationRepository.getAllDonations().size()); + + Donation donation = donationRepository.getAllDonations().values().iterator().next(); + assertEquals(customer.getUserId(), donation.userId()); + assertEquals(101, donation.organizationId()); + assertEquals(amount, donation.amount()); + assertEquals(paymentMethod, donation.paymentMethod()); + assertTrue(donation.date().before(Timestamp.from(Instant.now())) || + donation.date().equals(Timestamp.from(Instant.now()))); + } + + @Test + void testDonateWithMultipleDonations() { + donationService.donate(customer, 101, new BigDecimal("10.00"), "PayPal"); + donationService.donate(customer, 101, new BigDecimal("25.00"), "Card"); + assertEquals(2, donationRepository.getAllDonations().size()); + } +} \ No newline at end of file diff --git a/src/test/java/edu/group5/app/model/organization/OrganizationRepositoryTest.java b/src/test/java/edu/group5/app/model/organization/OrganizationRepositoryTest.java index 1913988..7a5ece5 100644 --- a/src/test/java/edu/group5/app/model/organization/OrganizationRepositoryTest.java +++ b/src/test/java/edu/group5/app/model/organization/OrganizationRepositoryTest.java @@ -69,4 +69,69 @@ void getTrustedOrganizations_OnlyReturnsTrustedOrganizations() { assertFalse(trusted.containsKey(4)); assertTrue(trusted.values().stream().allMatch(Organization::trusted)); } + + @Test + void testFindByOrgNumberReturnsOrganization() { + assertEquals(new Organization(1, "Trusted Org1", true, + "org.com", true, "Information about Trusted Org1"), + repository.findByOrgNumber(1)); + } + + @Test + void testFindByOrgNumberIfOrgNumberIsIllegal() { + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> repository.findByOrgNumber(-1)); + assertEquals("The Organization number must be a positive integer", exception.getMessage()); + } + + @Test + void testFindByOrgNumberIfOrgNumberNotFound() { + assertNull(repository.findByOrgNumber(999)); + } + + @Test + void testFindByOrgNameReturnsOrganization() { + assertEquals(new Organization(1, "Trusted Org1", true, + "org.com", true, "Information about Trusted Org1"), + repository.findByOrgName("Trusted Org1")); + } + + @Test + void testFindByOrgNameIfNameIsIllegalThrowsException() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> repository.findByOrgName(null)); + assertEquals("The name cannot be null", exception.getMessage()); + + IllegalArgumentException exception2 = assertThrows(IllegalArgumentException.class, + () -> repository.findByOrgName("")); + assertEquals("The name cannot be null", exception2.getMessage()); + } + + @Test + void testFindByOrgNameIfNameNotFound() { + assertNull(repository.findByOrgName("Nonexistent Org")); + } + + @Test + void testFindByOrgNameIsCaseInsensitive() { + assertEquals(new Organization(1, "Trusted Org1", true, + "org.com", true, "Information about Trusted Org1"), + repository.findByOrgName("trusted org1")); + } + + @Test + void testExportAllOrganizations() { + Object[] allOrgs = repository.export(); + assertEquals(4, allOrgs.length); + } + + @Test + void testExportAllOrganizationsThrowsWhenRepositoryIsEmpty() { + OrganizationRepository emptyRepo = new OrganizationRepository(new Object[0]); + IllegalStateException exception = assertThrows( + IllegalStateException.class, () -> emptyRepo.export() + ); + assertEquals("The repository is empty", exception.getMessage()); + } } \ No newline at end of file diff --git a/src/test/java/edu/group5/app/model/user/CustomerTest.java b/src/test/java/edu/group5/app/model/user/CustomerTest.java index 8678fc9..bd607f5 100644 --- a/src/test/java/edu/group5/app/model/user/CustomerTest.java +++ b/src/test/java/edu/group5/app/model/user/CustomerTest.java @@ -142,4 +142,68 @@ void verifyPasswordReturnsFalseForEmptyPassword() { assertFalse(user.verifyPassword("")); } + + @Test + void getRoleReturnsCustomer() { + User user = new Customer(testUserId, testFirstName, + testLastName, testEmail, testPasswordHash); + assertEquals(UserRepository.ROLE_CUSTOMER, user.getRole()); + } + + @Test + void addPreferenceAddsOrganizationNumber() { + Customer customer = new Customer(testUserId, testFirstName, testLastName, testEmail, testPasswordHash); + customer.addPreference(123); + assertTrue(customer.getPreferences().contains(123)); + } + + @Test + void addPreferenceWithNegativeOrgNumberThrowsException() { + Customer customer = new Customer(testUserId, testFirstName, testLastName, testEmail, testPasswordHash); + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> customer.addPreference(-1) + ); + assertEquals("Organization number must be a positive integer", exception.getMessage()); + } + + @Test + void addPreferenceWithExistingOrgNumberThrowsException() { + Customer customer = new Customer(testUserId, testFirstName, testLastName, testEmail, testPasswordHash); + customer.addPreference(123); + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> customer.addPreference(123) + ); + assertEquals("Organization number already in preferences", exception.getMessage()); + } + + @Test + void removePreferenceRemovesOrganizationNumber() { + Customer customer = new Customer(testUserId, testFirstName, testLastName, testEmail, testPasswordHash); + customer.addPreference(123); + customer.removePreference(123); + assertFalse(customer.getPreferences().contains(123)); + } + + @Test + void removePreferenceWithNegativeOrgNumberThrowsException() { + Customer customer = new Customer(testUserId, testFirstName, testLastName, testEmail, testPasswordHash); + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> customer.removePreference(-1) + ); + assertEquals("Organization number must be a positive integer", exception.getMessage()); + } + + @Test + void removePreferenceWithNonExistingOrgNumberThrowsException() { + Customer customer = new Customer(testUserId, testFirstName, testLastName, testEmail, testPasswordHash); + customer.addPreference(123); + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> customer.removePreference(456) + ); + assertEquals("Organization number not found in preferences", exception.getMessage()); + } } diff --git a/src/test/java/edu/group5/app/model/user/UserRepositoryTest.java b/src/test/java/edu/group5/app/model/user/UserRepositoryTest.java new file mode 100644 index 0000000..1b19c88 --- /dev/null +++ b/src/test/java/edu/group5/app/model/user/UserRepositoryTest.java @@ -0,0 +1,124 @@ +package edu.group5.app.model.user; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class UserRepositoryTest { + +private UserRepository repo; +private List rows; + + @BeforeEach + void setUp() { + rows = new ArrayList<>(); + rows.add(new Object[]{1, "Customer", "John", "Cena", "john@example.com", "hashedpass"}); + rows.add(new Object[]{2, "Customer", "Jane", "Doe", "jane@example.com", "hashedpass"}); + repo = new UserRepository(rows); + } + + @Test + void constructorThrowsIfNull() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> new UserRepository(null)); + assertEquals("The list of rows cannot be null", ex.getMessage()); + } + + @Test + void constructorThrowsIfInvalidRowLength() { + List invalid = new ArrayList<>(); + invalid.add(new Object[]{1, "Customer"}); + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> new UserRepository(invalid)); + assertEquals("Each row must contain exactly 6 elements", ex.getMessage()); + } + + @Test + void constructorThrowsIfUnknownRole() { + List badRole = new ArrayList<>(); + badRole.add(new Object[]{3, "Admin", "Bob", "Smith", "bob@example.com", "pass"}); + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> new UserRepository(badRole)); + assertEquals("Unknown role: Admin", ex.getMessage()); + } + + @Test + void addUserSuccessfully() { + Customer user = new Customer(3, "Bob", "Smith", "bob@example.com", "pass"); + assertTrue(repo.addUser(user)); + assertEquals(3, repo.getUsers().size()); + } + + @Test + void addUserNullThrows() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> repo.addUser(null)); + assertEquals("User cannot be null", ex.getMessage()); + } + + @Test + void addUserDuplicateReturnsFalse() { + Customer user = new Customer(1, "John", "Cena", "john@example.com", "pass"); + assertFalse(repo.addUser(user)); + } + + @Test + void findUserByEmailSuccessfully() { + User u = repo.findUserByEmail("jane@example.com"); + assertEquals("Jane", u.getFirstName()); + } + + @Test + void findUserByEmailReturnsNullIfNotFound() { + assertNull(repo.findUserByEmail("notfound@example.com")); + } + + @Test + void findUserByEmailThrowsIfNullOrEmpty() { + IllegalArgumentException ex1 = assertThrows(IllegalArgumentException.class, + () -> repo.findUserByEmail(null)); + assertEquals("Email cannot be null or empty", ex1.getMessage()); + + IllegalArgumentException ex2 = assertThrows(IllegalArgumentException.class, + () -> repo.findUserByEmail(" ")); + assertEquals("Email cannot be null or empty", ex2.getMessage()); + } + +@Test +void getUserByIdSuccessfully() { + User u = repo.getUserById(1); + assertEquals("John", u.getFirstName()); + } + +@Test +void getUserByIdReturnsNullIfNotFound() { + assertNull(repo.getUserById(999)); + } + + @Test + void getUserByIdThrowsIfNonPositive() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> repo.getUserById(0)); + assertEquals("User ID must be positive", ex.getMessage()); + } + + + @Test + void getNextUserIdReturnsNextAvailable() { + int nextId = repo.getNextUserId(); + assertEquals(3, nextId); + } + + @Test + void exportContainsAllUsers() { + List exported = repo.export(); + assertEquals(2, exported.size()); + assertEquals(1, exported.get(0)[0]); + assertEquals(2, exported.get(1)[0]); + assertEquals("Customer", exported.get(0)[1]); + } +} \ No newline at end of file diff --git a/src/test/java/edu/group5/app/model/user/UserServiceTest.java b/src/test/java/edu/group5/app/model/user/UserServiceTest.java new file mode 100644 index 0000000..57d4bf2 --- /dev/null +++ b/src/test/java/edu/group5/app/model/user/UserServiceTest.java @@ -0,0 +1,108 @@ +package edu.group5.app.model.user; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +public class UserServiceTest { + private UserRepository repo; + private UserService service; + + @BeforeEach + void setUp() { + List rows = new ArrayList<>(); + rows.add(new Object[]{1, "Customer", "John", "Cena", "john.cena@example.com", "$2a$10$hashed"}); + rows.add(new Object[]{2, "Customer", "Jane", "Doe", "jane.doe@example.com", "$2a$10$hashed"}); + repo = new UserRepository(rows); + service = new UserService(repo); + } + @Test + void constructorthrowsIfNull() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> new UserService(null)); + assertEquals("UserRepository cannot be null", ex.getMessage()); + } + + @Test + void registerUserValid() { + boolean result = service.registerUser("Customer", "Alice", "Smith", + "alice@example.com", "$2a$10$hashed"); + assertTrue(result); + assertEquals(3, repo.getUsers().size()); + } + + @Test + void registerUserRoleCaseInsensitive() { + boolean result = service.registerUser("customer", "Bob", "Smith", + "bob@example.com", "$2a$10$hashed"); + assertTrue(result); + } + + @Test + void registerUserInvalidInputsReturnFalse() { + assertFalse(service.registerUser(null, "A", "B", "a@b.com", "pass")); + assertFalse(service.registerUser("Customer", null, "B", "a@b.com", "pass")); + assertFalse(service.registerUser("Customer", "A", null, "a@b.com", "pass")); + assertFalse(service.registerUser("Customer", "A", "B", null, "pass")); + assertFalse(service.registerUser("Customer", "A", "B", "a@b.com", null)); + assertFalse(service.registerUser("", "A", "B", "a@b.com", "pass")); + assertFalse(service.registerUser("Customer", " ", "B", "a@b.com", "pass")); + } + + @Test + void registerUserDuplicateEmailAllowedInCurrentCode() { + boolean result = service.registerUser("Customer", "John", "Cena", + "john.cena@example.com", "$2a$10$hashed"); + assertTrue(result); + assertEquals(3, repo.getUsers().size()); + } + + @Test + void registerUserInvalidRoleReturnsFalse() { + boolean result = service.registerUser("Admin", "X", "Y", "x@y.com", "pass"); + assertFalse(result); + } + + @Test + void loginValidPassword() { + String plainPassword = "password123"; + BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); + String hashedPassword = encoder.encode(plainPassword); + User testUser = new Customer(10, "Test", "User", "test@example.com", hashedPassword); + repo.addUser(testUser); + + boolean result = service.login("test@example.com", plainPassword); + assertTrue(result); + } + + @Test + void loginInvalidPassword() { + String plainPassword = "password123"; + BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); + String hashedPassword = encoder.encode(plainPassword); + User testUser = new Customer(10, "Test", "User", "test@example.com", hashedPassword); + repo.addUser(testUser); + + boolean result = service.login("test@example.com", "wrongpass"); + assertFalse(result); + } + + @Test + void loginInvalidEmail() { + boolean result = service.login("nonexist@example.com", "password"); + boolean result2 = service.login(null, "password"); + boolean result3 = service.login(" ", "password"); + assertFalse(result); + assertFalse(result2); + assertFalse(result3); + } +} \ No newline at end of file