diff --git a/src/main/java/edu/group5/app/App.java b/src/main/java/edu/group5/app/App.java index b9d67da..3aea332 100644 --- a/src/main/java/edu/group5/app/App.java +++ b/src/main/java/edu/group5/app/App.java @@ -11,6 +11,7 @@ import edu.group5.app.model.user.UserService; import edu.group5.app.model.wrapper.DbWrapper; import edu.group5.app.model.wrapper.OrgApiWrapper; +import edu.group5.app.view.aboutuspage.AboutUsView; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.image.Image; @@ -35,6 +36,7 @@ public class App extends Application { NavigationController nav; private Logger logger; + static int MAX_RETRIES = 3; @Override public void init() { @@ -44,8 +46,14 @@ public void init() { this.dbWrapper = new DbWrapper(false); OrgApiWrapper orgApiWrapper = new OrgApiWrapper("https://app.innsamlingskontrollen.no/api/public/v1/all"); - while (!dbWrapper.connect()) { + int retries = 0; + while (!dbWrapper.connect() && retries < MAX_RETRIES) { this.logger.warning("Failed to connect to database"); + retries++; + } + if (retries == MAX_RETRIES) { + this.logger.severe("Failed to connect to database after " + MAX_RETRIES + " attempts"); + throw new RuntimeException("Failed to connect to database"); } // Load data from database @@ -75,7 +83,7 @@ public void init() { OrganizationService organizationService = new OrganizationService(organizationRepository, orgScraper); this.root = new BorderPane(); this.appState = new AppState(); - this.nav = new NavigationController(root, appState, userService, donationService, organizationService); + this.nav = new NavigationController(root, appState, userService, donationService, organizationService, this.getHostServices()); } @Override diff --git a/src/main/java/edu/group5/app/control/AuthController.java b/src/main/java/edu/group5/app/control/AuthController.java index 249a436..7c34afd 100644 --- a/src/main/java/edu/group5/app/control/AuthController.java +++ b/src/main/java/edu/group5/app/control/AuthController.java @@ -3,14 +3,12 @@ import edu.group5.app.model.AppState; import edu.group5.app.model.user.User; import edu.group5.app.model.user.UserService; +import edu.group5.app.utils.ParameterValidator; import edu.group5.app.view.loginpage.LoginPageView; import edu.group5.app.view.loginpage.SignUpPageView; import javafx.scene.control.Alert; -import javafx.scene.control.Button; import javafx.scene.control.ButtonType; -import java.util.Arrays; - import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; /** @@ -31,11 +29,31 @@ public class AuthController { private final UserService userService; public AuthController(AppState appState, NavigationController nav, UserService userService) { + ParameterValidator.objectChecker(appState, "AppState"); + ParameterValidator.objectChecker(nav, "NavigationController"); + ParameterValidator.objectChecker(userService, "UserService"); this.appState = appState; this.nav = nav; this.userService = userService; } + + /** + * Sets the current logged-in user. + * @param user the user to set as current + */ + public void setCurrentUser(User user) { + appState.setCurrentUser(user); + } + + /** + * Gets the current logged-in user. + * @return the current user, or null if no user logged in + */ + public User getCurrentUser() { + return appState.getCurrentUser(); + } + /** * Handles the registration of a {@link User}. * @@ -69,7 +87,7 @@ public void handleSignUp(SignUpPageView view, String firstName, String lastName, // Clears password char array after creating a hash. String hashedPassword = encoder.encode(new String(passwordChars)); for (int i = 0; i < passwordChars.length; i++) { - passwordChars[0] = '0'; + passwordChars[i] = '\u0000'; } Alert privacyPolicy = new Alert(Alert.AlertType.CONFIRMATION); @@ -86,9 +104,8 @@ public void handleSignUp(SignUpPageView view, String firstName, String lastName, if (success) { User user = userService.getUserByEmail(email); - - appState.setCurrentUser(user); - nav.showHomePage(); + appState.setCurrentUser(user); + nav.showHomePage(); } else { view.showError("Registration failed. Email may already be in use."); } diff --git a/src/main/java/edu/group5/app/control/DonationController.java b/src/main/java/edu/group5/app/control/DonationController.java index 8a8ec79..07af6fd 100644 --- a/src/main/java/edu/group5/app/control/DonationController.java +++ b/src/main/java/edu/group5/app/control/DonationController.java @@ -6,6 +6,7 @@ import edu.group5.app.model.organization.Organization; import edu.group5.app.model.user.Customer; import edu.group5.app.model.user.User; +import edu.group5.app.utils.ParameterValidator; import java.math.BigDecimal; import java.util.HashSet; @@ -34,11 +35,46 @@ public class DonationController { private final DonationService service; public DonationController(AppState appState, NavigationController nav, DonationService service) { + ParameterValidator.objectChecker(appState, "AppState"); + ParameterValidator.objectChecker(nav, "NavigationController"); + ParameterValidator.objectChecker(service, "DonationService"); this.appState = appState; this.nav = nav; this.service = service; } + /** + * Sets the current donation amount. + * @param amount the amount to donate + */ + public void setDonationAmount(BigDecimal amount) { + appState.setCurrentDonationAmount(amount); + } + + /** + * Gets the current donation amount. + * @return the donation amount + */ + public BigDecimal getDonationAmount() { + return appState.getCurrentDonationAmount(); + } + + /** + * Sets the current payment method. + * @param paymentMethod the payment method + */ + public void setPaymentMethod(String paymentMethod) { + appState.setCurrentPaymentMethod(paymentMethod); + } + + /** + * Gets the current payment method. + * @return the payment method + */ + public String getPaymentMethod() { + return appState.getCurrentPaymentMethod(); + } + /** * Retrieves all donations made by a specific user. * @@ -46,7 +82,8 @@ public DonationController(AppState appState, NavigationController nav, DonationS * @return a map of donations. */ public Map getUserDonations(int userId) { - return service.getDonationRepository().filterByUser(userId); + ParameterValidator.intChecker(userId, "User ID"); + return service.getUserDonations(userId); } /** @@ -56,7 +93,11 @@ public Map getUserDonations(int userId) { * @return a set of organization IDs */ public Set getUniqueOrganizationIDs() { - Map userDonations = getUserDonations(appState.getCurrentUser().getUserId()); + User currentUser = appState.getCurrentUser(); + if (currentUser == null) { + throw new IllegalStateException("No user logged in"); + } + Map userDonations = getUserDonations(currentUser.getUserId()); Set uniqueOrganizations = new HashSet<>(); for (Donation donation : userDonations.values()) { @@ -146,19 +187,26 @@ private void handleDonate() { ); if (success) { - System.out.println("Donation created: " + amount + " kr to " + currentOrg.name() + ", with payment method: " + paymentMethod); + System.out.println("Donation created: " + + amount + " kr to " + currentOrg.name() + + ", with payment method: " + paymentMethod); } else { System.err.println("Failed to create donation"); } // Clear donation session state appState.setCurrentDonationAmount(null); + // Clear org + appState.setCurrentOrganization(null); + // Clear payment method + appState.setCurrentPaymentMethod(null); // Navigate to payment complete nav.showPaymentCompletePage(); } private void showError(String message) { + ParameterValidator.stringChecker(message, "message"); Alert errorAlert = new Alert(Alert.AlertType.WARNING); errorAlert.setTitle("Donation Error"); errorAlert.setHeaderText("Cannot Process Donation"); diff --git a/src/main/java/edu/group5/app/control/NavigationController.java b/src/main/java/edu/group5/app/control/NavigationController.java index 7bfbc8c..8bbbbb3 100644 --- a/src/main/java/edu/group5/app/control/NavigationController.java +++ b/src/main/java/edu/group5/app/control/NavigationController.java @@ -4,7 +4,9 @@ import edu.group5.app.model.donation.DonationService; import edu.group5.app.model.organization.OrganizationService; import edu.group5.app.model.user.UserService; +import edu.group5.app.utils.ParameterValidator; import edu.group5.app.view.Header; +import edu.group5.app.view.aboutuspage.AboutUsView; import edu.group5.app.view.causespage.CausesPageView; import edu.group5.app.view.donationpage.DonationPageView; import edu.group5.app.view.donationpage.PaymentCompletePageView; @@ -14,10 +16,23 @@ import edu.group5.app.view.loginpage.SignUpPageView; import edu.group5.app.view.organizationpage.OrganizationPageView; import edu.group5.app.view.userpage.UserPageView; +import javafx.application.HostServices; +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; +import javafx.scene.control.Hyperlink; +import javafx.scene.control.Label; import javafx.scene.layout.BorderPane; +import javafx.scene.layout.VBox; /** - * Controller responsible for navigating between views within the root node. + * Controller responsible for handling navigation between different pages of the application. + * Coordinates between {@link AppState}, {@link AuthController}, {@link DonationController}, + * {@link OrganizationController} and the various views to manage page transitions and state updates. + *

+ * Provides methods to navigate to the home page, login page, sign-up page, causes page, + * organization page, donation page, payment complete page and user profile page. Each method updates the + * top header and center content of the main application layout accordingly. + *

*/ public class NavigationController { private final BorderPane root; @@ -25,64 +40,111 @@ public class NavigationController { private final LoginHeader loginHeader; private final AppState appState; + private final HostServices hostServices; private final AuthController authController; private final DonationController donationController; private final OrganizationController organizationController; - public NavigationController(BorderPane root, AppState appState, UserService userService, DonationService donationService, OrganizationService organizationService) { + public NavigationController(BorderPane root, AppState appState, UserService userService, + DonationService donationService, OrganizationService organizationService, HostServices hostServices) { + ParameterValidator.objectChecker(root, "Root BorderPane"); + ParameterValidator.objectChecker(appState, "AppState"); + ParameterValidator.objectChecker(userService, "UserService"); + ParameterValidator.objectChecker(donationService, "DonationService"); + ParameterValidator.objectChecker(organizationService, "OrganizationService"); + ParameterValidator.objectChecker(hostServices, "HostServices"); + this.root = root; this.header = new Header(this); this.loginHeader = new LoginHeader(); - this.appState = appState; + this.hostServices = hostServices; this.authController = new AuthController(appState, this, userService); this.donationController = new DonationController(appState, this, donationService); - this.organizationController = new OrganizationController(organizationService); + this.organizationController = new OrganizationController(appState, organizationService); } + /** + * Navigates to the home page by setting the top header and center content of the main layout. + * The home page serves as the landing page of the application, providing an overview and access to various features. + */ public void showHomePage() { root.setTop(header); - root.setCenter(new HomePageView(appState, this)); + root.setCenter(new HomePageView(this)); } + /** + * Navigates to the login page by setting the top header and center content of the main layout. + * The login page allows existing users to enter their credentials and access their account. + */ public void showLoginPage() { root.setTop(loginHeader); - root.setCenter(new LoginPageView(appState, this, authController)); + root.setCenter(new LoginPageView(this, authController)); } + /** + * Navigates to the sign-up page by setting the top header and center content of the main layout. + * The sign-up page allows new users to create an account by providing their details. + */ public void showSignUpPage() { root.setTop(loginHeader); - root.setCenter(new SignUpPageView(appState, this, authController)); + root.setCenter(new SignUpPageView(this, authController)); } + /** + * Navigates to the payment complete page by setting the top header and center content of the main layout. + * The payment complete page confirms the successful completion of a donation transaction. + */ public void showPaymentCompletePage() { root.setTop(header); root.setCenter(new PaymentCompletePageView(this)); } + /** + * Navigates to the causes page by setting the top header and center content of the main layout. + * The causes page allows users to browse and search for organizations they may want to donate to. + */ public void showCausesPage() { root.setTop(header); - root.setCenter(new CausesPageView(appState, this, organizationController)); + root.setCenter(new CausesPageView(this, organizationController)); } + /** + * Navigates to the organization page by setting the top header and center content of the main layout. + * The organization page provides detailed information about a specific organization, including its mission, + * impact, and donation options, allowing users to learn more before making a donation. + */ public void showOrganizationPage() { root.setTop(header); - root.setCenter(new OrganizationPageView(appState, this, donationController)); + root.setCenter(new OrganizationPageView(this, organizationController, donationController)); } + /** + * Navigates to the donation page by setting the top header and center content of the main layout. + * The donation page allows users to make new donations to selected organizations. + */ public void showDonationPage() { root.setTop(header); - root.setCenter(new DonationPageView(appState, this, donationController)); + root.setCenter(new DonationPageView(this, donationController)); } + /** + * Displays an "About Us" dialog with information about the application and its developers. + * The dialog includes a description of the app's mission and a hyperlink to the project's GitHub repository + */ public void showAboutUsPage() { - root.setTop(header); + new AboutUsView(hostServices).displayAboutUs(); } + /** + * Navigates to the user profile page by setting the top header and center content of the main layout. + * The user profile page allows users to view and manage their account information, + * donation history, and other personalized features. + */ public void showUserPage() { root.setTop(header); - root.setCenter(new UserPageView(appState, this, authController, donationController, organizationController)); + root.setCenter(new UserPageView(this, authController, donationController, organizationController)); } } diff --git a/src/main/java/edu/group5/app/control/OrganizationController.java b/src/main/java/edu/group5/app/control/OrganizationController.java index fb65127..6ffe830 100644 --- a/src/main/java/edu/group5/app/control/OrganizationController.java +++ b/src/main/java/edu/group5/app/control/OrganizationController.java @@ -1,22 +1,54 @@ package edu.group5.app.control; +import edu.group5.app.model.AppState; import edu.group5.app.model.organization.Organization; import edu.group5.app.model.organization.OrganizationService; +import edu.group5.app.utils.ParameterValidator; import java.util.Map; import java.util.concurrent.CompletableFuture; /** * Controller responsible for organization-related operations. + * + *

+ * Coordinates with {@link OrganizationService} to: + *

    + *
  • Retrieve organization information by ID
  • + *
  • Retrieve all trusted organizations
  • + *
+ *

*/ public class OrganizationController { + private final AppState appState; private final OrganizationService service; - public OrganizationController(OrganizationService service) { + public OrganizationController(AppState appState, OrganizationService service) { + ParameterValidator.objectChecker(appState, "AppState"); + ParameterValidator.objectChecker(service, "OrganizationService"); + this.appState = appState; this.service = service; } + + /** + * Sets the current selected organization. + * @param organization the organization to set as current + */ + public void setCurrentOrganization(Organization org) { + appState.setCurrentOrganization(org); + } + + /** + * Gets the current selected organization. + * @return the current organization, or null if none selected + */ + public Organization getCurrentOrganization() { + return appState.getCurrentOrganization(); + } + public Organization getOrganizationById(int orgId) { + ParameterValidator.intChecker(orgId, "Organization ID"); return service.findByOrgNumber(orgId); } diff --git a/src/main/java/edu/group5/app/model/AppState.java b/src/main/java/edu/group5/app/model/AppState.java index 73d5cfc..bd56ba0 100644 --- a/src/main/java/edu/group5/app/model/AppState.java +++ b/src/main/java/edu/group5/app/model/AppState.java @@ -4,41 +4,82 @@ import edu.group5.app.model.user.User; import java.math.BigDecimal; - +/** + * AppState class represents the current state of the application, including: + *
  • the current user
  • + *
  • the selected organization
  • + *
  • current donation amount
  • + *
  • current payment method
  • + *
    + * It serves as a centralized data store for the application's + * stateful information, allowing different components of the application to access and modify this information as needed. + */ public class AppState { private User currentUser; private BigDecimal currentDonationAmount; private Organization currentOrganization; private String currentDonation; + /** + * Gets the current user of the application. + * @return the current user + */ public User getCurrentUser() { return this.currentUser; } + /** + * Sets the current user of the application. + * @param user the user to set as the current user + */ public void setCurrentUser(User user) { this.currentUser = user; } + /** + * Gets the selected organization. + * @return the selected organization + */ public Organization getCurrentOrganization() { return this.currentOrganization; } + /** + * Sets the selected organization. + * @param organization the organization to set as the current organization + */ public void setCurrentOrganization(Organization organization) { this.currentOrganization = organization; } + /** + * Gets the current donation amount. + * @return the current donation amount + */ public BigDecimal getCurrentDonationAmount() { return this.currentDonationAmount; } + /** + * Sets the current donation amount. + * @param amount the amount to set as the current donation amount + */ public void setCurrentDonationAmount(BigDecimal amount) { this.currentDonationAmount = amount; } + /** + * Gets the current payment method. + * @return the current payment method + */ public String getCurrentPaymentMethod() { return this.currentDonation; } + /** + * Sets the current payment method. + * @param paymentMethod the payment method to set as the current payment method + */ public void setCurrentPaymentMethod(String paymentMethod){ this.currentDonation = paymentMethod; } diff --git a/src/main/java/edu/group5/app/model/DBRepository.java b/src/main/java/edu/group5/app/model/DBRepository.java index 3cd1fa9..d36d0e2 100644 --- a/src/main/java/edu/group5/app/model/DBRepository.java +++ b/src/main/java/edu/group5/app/model/DBRepository.java @@ -2,6 +2,9 @@ import java.util.HashMap; import java.util.Map; + +import edu.group5.app.utils.ParameterValidator; + import java.util.List; /** @@ -22,12 +25,24 @@ public abstract class DBRepository extends Repository { * @param content the HashMap used to store repository entities */ protected DBRepository(Map content) { + ParameterValidator.objectChecker(content, "Content"); super(content); this.contentLock = new HashMap<>(); } + /** + * Updates the content lock with the current state of the content. + * This method should be called whenever the content is modified to ensure + * that the content lock reflects the latest state of the repository. + */ protected abstract void updateContentLock(); + /** + * Adds a new entity to the repository content. + * @param value the entity to be added to the repository + * @return true if the entity was successfully added, false otherwise + * @throws IllegalArgumentException if the value is null + */ public abstract boolean addContent(V value); /** diff --git a/src/main/java/edu/group5/app/model/Repository.java b/src/main/java/edu/group5/app/model/Repository.java index 032f307..6dd425e 100644 --- a/src/main/java/edu/group5/app/model/Repository.java +++ b/src/main/java/edu/group5/app/model/Repository.java @@ -2,8 +2,15 @@ import java.util.Map; +import edu.group5.app.utils.ParameterValidator; + /** - * Represents a repository. + * Abstract base class for repositories. + *

    + * Repositories are responsible for managing collections of entities, providing + * basic operations for accessing and manipulating these entities. The specific + * type of entities and the underlying data structure are defined by subclasses. + *

    */ public abstract class Repository { protected final Map content; @@ -13,12 +20,13 @@ public abstract class Repository { * @param content the underlying data structure used to store entities */ protected Repository(Map content) { + ParameterValidator.objectChecker(content, "content"); this.content = content; } /** - * Gets the content of the repo - * @return content of the repo + * Gets the content of the repository. + * @return the content of the repository */ public Map getContent() { return content; 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 a55ea74..f1102ba 100644 --- a/src/main/java/edu/group5/app/model/donation/DonationRepository.java +++ b/src/main/java/edu/group5/app/model/donation/DonationRepository.java @@ -1,6 +1,7 @@ package edu.group5.app.model.donation; import edu.group5.app.model.DBRepository; +import edu.group5.app.utils.ParameterValidator; import java.util.Comparator; import java.util.HashMap; @@ -32,9 +33,7 @@ public class DonationRepository extends DBRepository { */ public DonationRepository(List rows) { super(new HashMap<>()); - if (rows == null) { - throw new IllegalArgumentException("The list of rows cannot be null"); - } + ParameterValidator.objectChecker(rows, "List of donation rows"); this.content = new HashMap<>(); for (Object[] row : rows) { if (row == null || row.length != 6) { @@ -88,9 +87,7 @@ public List export() { * @throws IllegalArgumentException if the donationId is not positive */ public Donation getDonationById(int donationId) { - if (donationId <= 0) { - throw new IllegalArgumentException("Donation ID must be positive"); - } + ParameterValidator.intChecker(donationId, "Donation ID"); return content.get(donationId); } @@ -102,7 +99,7 @@ public Donation getDonationById(int donationId) { */ 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); @@ -122,9 +119,7 @@ public Map getAllDonations() { */ @Override public boolean addContent(Donation donation) { - if (donation == null) { - throw new IllegalArgumentException("Donation cannot be null"); - } + ParameterValidator.objectChecker(donation, "Donation"); if (content.containsKey(donation.donationId())) { return false; } @@ -180,9 +175,7 @@ public HashMap sortByAmount() { * @throws IllegalArgumentException if the orgNumber is not positive */ public HashMap filterByOrganization(int orgNumber) { - if (orgNumber <= 0) { - throw new IllegalArgumentException("Organization number must be positive"); - } + ParameterValidator.intChecker(orgNumber, "Organization number"); return content.entrySet() .stream() .filter(entry -> entry.getValue().organizationId() == orgNumber) @@ -201,9 +194,7 @@ public HashMap filterByOrganization(int orgNumber) { * @throws IllegalArgumentException if the userId is not positive */ public HashMap filterByUser(int userId) { - if (userId <= 0) { - throw new IllegalArgumentException("User ID must be positive"); - } + ParameterValidator.intChecker(userId, "User ID"); return content.entrySet().stream() .filter(entry -> entry.getValue().userId() == userId) .collect(Collectors.toMap( 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 690a632..5eb964b 100644 --- a/src/main/java/edu/group5/app/model/donation/DonationService.java +++ b/src/main/java/edu/group5/app/model/donation/DonationService.java @@ -1,11 +1,12 @@ package edu.group5.app.model.donation; import java.time.Instant; - +import java.util.Map; import java.math.BigDecimal; import java.sql.Timestamp; import edu.group5.app.model.organization.Organization; import edu.group5.app.model.organization.OrganizationRepository; import edu.group5.app.model.user.Customer; +import edu.group5.app.utils.ParameterValidator; /** * DonationService class provides functionality for handling donations in the system. @@ -27,32 +28,28 @@ public class DonationService { */ public DonationService(DonationRepository donationRepository, OrganizationRepository organizationRepository) { - if (donationRepository == null) { - throw new IllegalArgumentException("DonationRepository cannot be null"); - } - if (organizationRepository == null) { - throw new IllegalArgumentException("OrganizationRepository cannot be null"); - } + ParameterValidator.objectChecker(donationRepository, "DonationRepository"); + ParameterValidator.objectChecker(organizationRepository, "OrganizationRepository"); this.donationRepository = donationRepository; this.organizationRepository = organizationRepository; } /** - * Getter for the DonationRepository used by this service. - * This method allows access to the donation repository for managing donation records and retrieving donation information. - * @return the DonationRepository instance used by this service + * Gets all donations made by a specific user. + * @param userId the ID of the user + * @return a map of donations made by that user */ - public DonationRepository getDonationRepository() { - return this.donationRepository; + public Map getUserDonations(int userId) { + return donationRepository.filterByUser(userId); } /** - * Getter for the OrganizationRepository used by this service. - * This method allows access to the organization repository for validating organization information when processing donations. - * @return the OrganizationRepository instance used by this service + * Gets all donations to a specific organization. + * @param organizationId the organization ID + * @return map of donations to that organization */ - public OrganizationRepository getOrganizationRepository() { - return this.organizationRepository; + public Map getOrganizationDonations(int organizationId) { + return donationRepository.filterByOrganization(organizationId); } /** diff --git a/src/main/java/edu/group5/app/model/organization/Organization.java b/src/main/java/edu/group5/app/model/organization/Organization.java index 7016567..4c66be0 100644 --- a/src/main/java/edu/group5/app/model/organization/Organization.java +++ b/src/main/java/edu/group5/app/model/organization/Organization.java @@ -2,6 +2,8 @@ import java.util.Objects; +import edu.group5.app.utils.ParameterValidator; + /** * Represents an organization. * @@ -45,9 +47,7 @@ public record Organization( */ public Organization(int orgNumber, String name, boolean trusted, String websiteUrl, boolean isPreApproved, String description, String logoUrl) { - if (orgNumber < 0) { - throw new IllegalArgumentException("orgNumber cannot be negative"); - } + ParameterValidator.intChecker(orgNumber, "Organization number"); this.orgNumber = orgNumber; this.name = Objects.requireNonNull(name, "name cannot be null"); this.trusted = trusted; 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 61155b7..5da9a97 100644 --- a/src/main/java/edu/group5/app/model/organization/OrganizationRepository.java +++ b/src/main/java/edu/group5/app/model/organization/OrganizationRepository.java @@ -1,6 +1,7 @@ package edu.group5.app.model.organization; import edu.group5.app.model.Repository; +import edu.group5.app.utils.ParameterValidator; import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; @@ -30,12 +31,8 @@ public class OrganizationRepository extends Repository { public OrganizationRepository(Object[] input, OrganizationScraper scraper) { super(new HashMap<>()); this.grandMap = new HashMap<>(); - if (input == null) { - throw new IllegalArgumentException("The input cannot be null"); - } - if (scraper == null) { - throw new IllegalArgumentException("The scraper cannot be null"); - } + ParameterValidator.objectChecker(input, "Input data"); + ParameterValidator.objectChecker(scraper, "Scraper"); this.scraper = scraper; ObjectMapper mapper = new ObjectMapper(); @@ -44,6 +41,11 @@ public OrganizationRepository(Object[] input, OrganizationScraper scraper) { HashMap contentMap = mapper.convertValue(obj, new TypeReference>() {}); + Object orgNumberObj = contentMap.get("org_number"); + if (orgNumberObj == null) { + System.err.println("Skipping organization: missing org_number"); + continue; + } String orgNumberStr = ((String) contentMap.get("org_number")).replaceAll("\\s", ""); int orgNumber = Integer.parseInt(orgNumberStr); String name = (String) contentMap.get("name"); @@ -101,9 +103,7 @@ public Map getTrustedOrganizations() { * @throws IllegalArgumentException if the organization number is not a positive integer */ public Organization findByOrgNumber(int orgNumber) { - if (orgNumber <= 0) { - throw new IllegalArgumentException("The Organization number must be a positive integer"); - } + ParameterValidator.intChecker(orgNumber, "Organization number"); return grandMap.get(orgNumber); } @@ -114,9 +114,7 @@ public Organization findByOrgNumber(int orgNumber) { * @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"); - } + ParameterValidator.stringChecker(name, "Organization name"); return grandMap.values().stream() .filter(org -> org.name().equalsIgnoreCase(name)) .findFirst() diff --git a/src/main/java/edu/group5/app/model/organization/OrganizationScraper.java b/src/main/java/edu/group5/app/model/organization/OrganizationScraper.java index 6a8c230..ad93736 100644 --- a/src/main/java/edu/group5/app/model/organization/OrganizationScraper.java +++ b/src/main/java/edu/group5/app/model/organization/OrganizationScraper.java @@ -3,13 +3,9 @@ import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; -import org.jsoup.nodes.TextNode; -import org.jsoup.select.Elements; - import java.util.stream.Collectors; - -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * Handles web scraping of organization information from Innsamlingskontrollen. @@ -17,8 +13,8 @@ * All results are cached to avoid redundant network requests. */ public class OrganizationScraper { - private final Map logoCache = new HashMap<>(); - private final Map descriptionCache = new HashMap<>(); + private final Map logoCache = new ConcurrentHashMap<>(); + private final Map descriptionCache = new ConcurrentHashMap<>(); /** * Fetches the description for the given URL by scraping all text content @@ -35,11 +31,7 @@ public class OrganizationScraper { * @return the description text, or null if not found or pageUrl is invalid */ public String fetchDescription(String pageUrl) { - if (pageUrl == null || pageUrl.isBlank()) { - return null; - } - - if (descriptionCache.containsKey(pageUrl)) { + if (pageUrl != null && descriptionCache.containsKey(pageUrl)) { return descriptionCache.get(pageUrl); } @@ -88,11 +80,7 @@ public String fetchDescription(String pageUrl) { * @return the absolute logo URL, or null if not found or pageUrl is invalid */ public String fetchLogoUrl(String pageUrl) { - if (pageUrl == null || pageUrl.isBlank()) { - return null; - } - - if (logoCache.containsKey(pageUrl)) { + if (pageUrl != null && logoCache.containsKey(pageUrl)) { return logoCache.get(pageUrl); } diff --git a/src/main/java/edu/group5/app/model/organization/OrganizationService.java b/src/main/java/edu/group5/app/model/organization/OrganizationService.java index e19c273..df4d04e 100644 --- a/src/main/java/edu/group5/app/model/organization/OrganizationService.java +++ b/src/main/java/edu/group5/app/model/organization/OrganizationService.java @@ -2,6 +2,8 @@ import java.util.stream.Collectors; +import edu.group5.app.utils.ParameterValidator; + import java.util.Comparator; import java.util.LinkedHashMap; import java.util.Map; @@ -27,24 +29,12 @@ public class OrganizationService { * @throws IllegalArgumentException if organizationRepository or scraper is null */ public OrganizationService(OrganizationRepository organizationRepository, OrganizationScraper scraper) { - if (organizationRepository == null) { - throw new IllegalArgumentException("OrganizationRepository cannot be null"); - } - if (scraper == null) { - throw new IllegalArgumentException("OrganizationScraper cannot be null"); - } + ParameterValidator.objectChecker(organizationRepository, "OrganizationRepository"); + ParameterValidator.objectChecker(scraper, "Scraper"); this.organizationRepository = organizationRepository; this.scraper = scraper; } - /** - * Getter for the OrganizationRepository used by this service. - * @return the OrganizationRepository instance used by this service - */ - public OrganizationRepository getOrganizationRepository() { - return this.organizationRepository; - } - /** * Retrieves all trusted organizations. * @return a map of trusted organizations by organization number @@ -71,6 +61,7 @@ public Organization findByOrgNumber(int orgNumber) { * @return the Organization if found, null otherwise */ public Organization findByOrgName(String name) { + ParameterValidator.stringChecker(name, "name"); return organizationRepository.findByOrgName(name); } 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 93f03ac..d04139d 100644 --- a/src/main/java/edu/group5/app/model/user/Customer.java +++ b/src/main/java/edu/group5/app/model/user/Customer.java @@ -1,6 +1,9 @@ package edu.group5.app.model.user; import java.util.List; + +import edu.group5.app.utils.ParameterValidator; + import java.util.ArrayList; /** @@ -38,9 +41,7 @@ public String getRole() { } public void addPreference(int orgNumber) { - if (orgNumber <= 0) { - throw new IllegalArgumentException("Organization number must be a positive integer"); - } + ParameterValidator.intChecker(orgNumber,"Organization number"); if (preferences.contains(orgNumber)) { throw new IllegalArgumentException("Organization number already in preferences"); } @@ -48,9 +49,7 @@ public void addPreference(int orgNumber) { } public void removePreference(int orgNumber) { - if (orgNumber <= 0) { - throw new IllegalArgumentException("Organization number must be a positive integer"); - } + ParameterValidator.intChecker(orgNumber, "Organization number"); if (!preferences.contains(orgNumber)) { throw new IllegalArgumentException("Organization number not found in preferences"); } 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 411538d..806f1f7 100644 --- a/src/main/java/edu/group5/app/model/user/User.java +++ b/src/main/java/edu/group5/app/model/user/User.java @@ -1,5 +1,8 @@ package edu.group5.app.model.user; + import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import edu.group5.app.utils.ParameterValidator; /** * User class represents a user in the system. It is an abstract class that will be extended by specific user types such as Donor, Recipient, and Admin. * Each user has a unique userId, a role that defines their permissions in the system, and personal information such as first name, last name, email, and password hash. @@ -27,21 +30,12 @@ public abstract class User { */ public User(int userId, String firstName, String lastName, String email, String passwordHash) { - if (userId <= 0) { - throw new IllegalArgumentException("User ID must be positive"); - } - if (firstName == null || firstName.trim().isEmpty()) { - throw new IllegalArgumentException("First name cannot be null or empty"); - } - if (lastName == null || lastName.trim().isEmpty()) { - throw new IllegalArgumentException("Last name cannot be null or empty"); - } - if (email == null || email.trim().isEmpty()) { - throw new IllegalArgumentException("Email cannot be null or empty"); - } - if (passwordHash == null || passwordHash.trim().isEmpty()) { - throw new IllegalArgumentException("Password hash cannot be null or empty"); - } + ParameterValidator.intChecker(userId, "User ID"); + ParameterValidator.stringChecker(firstName, "First name"); + ParameterValidator.stringChecker(lastName, "Last name"); + ParameterValidator.stringChecker(email, "Email"); + ParameterValidator.stringChecker(passwordHash, "Password hash"); + this.userId = userId; this.firstName = firstName.trim(); this.lastName = lastName.trim(); 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 193f433..07f9ca8 100644 --- a/src/main/java/edu/group5/app/model/user/UserRepository.java +++ b/src/main/java/edu/group5/app/model/user/UserRepository.java @@ -5,6 +5,7 @@ import java.util.Map; import edu.group5.app.model.DBRepository; +import edu.group5.app.utils.ParameterValidator; public class UserRepository extends DBRepository { public final static String ROLE_CUSTOMER = "Customer"; @@ -18,9 +19,7 @@ public class UserRepository extends DBRepository { */ public UserRepository(List rows) { super(new HashMap<>()); - if (rows == null) { - throw new IllegalArgumentException("The list of rows cannot be null"); - } + ParameterValidator.objectChecker(rows, "List of User rows"); for (Object[] row : rows) { if (row == null || row.length != 6) { throw new IllegalArgumentException("Each row must contain exactly 6 elements"); @@ -83,9 +82,7 @@ public HashMap getUsers() { * @throws IllegalArgumentException if the userId is not positive */ public User getUserById(int userId) { - if (userId <= 0) { - throw new IllegalArgumentException("User ID must be positive"); - } + ParameterValidator.intChecker(userId, "User ID"); return content.get(userId); } @@ -104,7 +101,7 @@ public int getNextUserId() { () -> new IllegalStateException("No keys found")); int nextId = maxKey + 1; return nextId; - } /* TODO change this when data database is introduced */ + } /** * Adds a new user to the repository @@ -120,9 +117,7 @@ public int getNextUserId() { */ @Override public boolean addContent(User user) { - if (user == null) { - throw new IllegalArgumentException("User cannot be null"); - } + ParameterValidator.objectChecker(user, "User"); if (content.containsKey(user.getUserId())) { return false; } @@ -138,9 +133,7 @@ public boolean addContent(User user) { * user exists */ public User findUserByEmail(String email) { - if (email == null || email.trim().isEmpty()) { - throw new IllegalArgumentException("Email cannot be null or empty"); - } + ParameterValidator.stringChecker(email, "Email"); return content.values().stream() .filter(user -> user.getEmail().equals(email)) .findFirst() 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 b237eae..6bee483 100644 --- a/src/main/java/edu/group5/app/model/user/UserService.java +++ b/src/main/java/edu/group5/app/model/user/UserService.java @@ -1,5 +1,7 @@ package edu.group5.app.model.user; +import edu.group5.app.utils.ParameterValidator; + /** * Service class for managing user-related operations, such as registration and * login. @@ -19,23 +21,10 @@ public class UserService { * @throws IllegalArgumentException if userRepository is null */ public UserService(UserRepository userRepository) { - if (userRepository == null) { - throw new IllegalArgumentException("UserRepository cannot be null"); - } + ParameterValidator.objectChecker(userRepository, "UserRepository"); this.userRepository = userRepository; } - /** - * Getter for the UserRepository used by this service. - * This method allows access to the user repository for managing user data and - * performing operations such as registration and login. - * - * @return the UserRepository instance used by this service - */ - public UserRepository getUserRepository() { - return this.userRepository; - } - /** * Registers a new user with the given information. Validates the input data and * creates a new User object @@ -62,8 +51,7 @@ public boolean registerUser(String role, String firstName, String lastName, firstName == null || firstName.trim().isEmpty() || lastName == null || lastName.trim().isEmpty() || email == null || email.trim().isEmpty() || - passwordHash == null || passwordHash.trim().isEmpty() || - this.getUserByEmail(email) != null) { + passwordHash == null || passwordHash.trim().isEmpty()) { return false; } User user; diff --git a/src/main/java/edu/group5/app/utils/ParameterValidator.java b/src/main/java/edu/group5/app/utils/ParameterValidator.java index 3bc3e5a..28a8dc0 100644 --- a/src/main/java/edu/group5/app/utils/ParameterValidator.java +++ b/src/main/java/edu/group5/app/utils/ParameterValidator.java @@ -24,7 +24,7 @@ public final class ParameterValidator { public static final void stringChecker(String stringArg, String variableName) throws IllegalArgumentException { nullCheck(stringArg, variableName); - if (stringArg.isBlank()) { + if (stringArg.trim().isBlank()) { throw new IllegalArgumentException(String.format("%s can't be blank", variableName)); } } @@ -104,7 +104,7 @@ public static final void exportChecker( if (!data.isEmpty()) { if (data.stream().anyMatch(i -> i.length != rowLength)) { throw new IllegalArgumentException( - String.format("%s's arrays must have a length of 6", dataName) + String.format("%s's arrays must have a length of %d", dataName, rowLength) ); } if (data.stream().anyMatch(i -> Arrays.asList(i).contains(null))) { diff --git a/src/main/java/edu/group5/app/view/aboutuspage/AboutUsView.java b/src/main/java/edu/group5/app/view/aboutuspage/AboutUsView.java new file mode 100644 index 0000000..f25a95e --- /dev/null +++ b/src/main/java/edu/group5/app/view/aboutuspage/AboutUsView.java @@ -0,0 +1,55 @@ +package edu.group5.app.view.aboutuspage; + +import edu.group5.app.utils.ParameterValidator; +import javafx.application.HostServices; +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; +import javafx.scene.control.Hyperlink; +import javafx.scene.control.Label; +import javafx.scene.layout.VBox; + +/** + * A view for displaying information about the "Help Me Help" application and its creators. + * The view is presented as an informational dialog that includes a description of the app's mission + * and a hyperlink to the project's GitHub repository for more details. This page serves to provide + * users with background information about the application and its development team. + */ +public class AboutUsView { + private HostServices hostServices; + + public AboutUsView(HostServices hostServices) { + ParameterValidator.objectChecker(hostServices, "HostServices"); + this.hostServices = hostServices; + } + + /** + * Displays the "About Us" information in an alert dialog. + */ + public void displayAboutUs() { + Alert aboutUs = new Alert(Alert.AlertType.INFORMATION); + aboutUs.setTitle("About us"); + aboutUs.setHeaderText("Help Me Help - About Us"); + + Label description = new Label( + "Help Me Help is a charity donation application designed to connect donors with organizations in need. " + + "Our mission is to make it easy for people to support causes they care about and make a positive impact in the world.\n\n" + + "This application was developed by Team 5 as part of a IDATT1005 course project at NTNU spring 2026." + ); + description.setWrapText(true); + description.maxWidthProperty().bind(aboutUs.getDialogPane().widthProperty().subtract(40)); + + Hyperlink websiteLink = new Hyperlink("For more information about the project, visit our GitHub repository"); + websiteLink.setOnAction(e -> hostServices.showDocument("https://git.ntnu.no/Group-5/Help-Me-Help")); + + VBox content = new VBox(10, description, websiteLink); + content.setPrefWidth(420); + + aboutUs.getDialogPane().setContentText(null); + aboutUs.getDialogPane().setContent(content); + + // Optional: single Close button instead of OK + aboutUs.getButtonTypes().setAll(ButtonType.CLOSE); + + aboutUs.showAndWait(); + } +} diff --git a/src/main/java/edu/group5/app/view/causespage/CausesPageView.java b/src/main/java/edu/group5/app/view/causespage/CausesPageView.java index 7db3ee9..0bdddbc 100644 --- a/src/main/java/edu/group5/app/view/causespage/CausesPageView.java +++ b/src/main/java/edu/group5/app/view/causespage/CausesPageView.java @@ -1,9 +1,9 @@ package edu.group5.app.view.causespage; import edu.group5.app.model.organization.Organization; +import edu.group5.app.utils.ParameterValidator; import edu.group5.app.control.NavigationController; import edu.group5.app.control.OrganizationController; -import edu.group5.app.model.AppState; import javafx.application.Platform; import javafx.scene.control.ScrollPane; import javafx.scene.control.TextField; @@ -33,15 +33,17 @@ * a fallback "no image" is displayed.

    */ public class CausesPageView extends BorderPane { - private final AppState appState; private final NavigationController nav; private final OrganizationController orgController; private GridPane organizationGrid; private Map allOrganizations; + private Map cardCache = new HashMap<>(); + + public CausesPageView(NavigationController nav, OrganizationController orgController) { + ParameterValidator.objectChecker(nav, "NavigationController"); + ParameterValidator.objectChecker(orgController, "OrganizationController"); - public CausesPageView(AppState appState, NavigationController nav, OrganizationController orgController) { - this.appState = appState; this.nav = nav; this.orgController = orgController; @@ -89,9 +91,9 @@ private BorderPane createBody() { card.updateLogo(entry.getValue().logoUrl()); } } - Organization currentOrg = appState.getCurrentOrganization(); + Organization currentOrg = orgController.getCurrentOrganization(); if (currentOrg != null && orgs.containsKey(currentOrg.orgNumber())) { - appState.setCurrentOrganization(orgs.get(currentOrg.orgNumber())); + orgController.setCurrentOrganization(orgs.get(currentOrg.orgNumber())); } }); }); @@ -160,7 +162,7 @@ private GridPane createOrganizationSection(String searchTerm) { ? org.logoUrl() : null; - OrganizationCard card = new OrganizationCard(appState, nav, org, img); + OrganizationCard card = new OrganizationCard(nav, orgController, org, img); grid.add(card, column, row); @@ -204,18 +206,41 @@ private Map filterOrganizations(String searchTerm) { )); } + private void updateOrganizationGrid(String searchTerm) { - if (organizationGrid == null) { - return; + if (organizationGrid == null) return; + + // Save existing cards into cache before clearing + for (var node : organizationGrid.getChildren()) { + if (node instanceof OrganizationCard card) { + cardCache.put(card.getOrganization().orgNumber(), card); + } } - + + Map filtered = filterOrganizations(searchTerm); + organizationGrid.getChildren().clear(); organizationGrid.getColumnConstraints().clear(); - - // Rebuild grid with filtered organizations - GridPane updated = createOrganizationSection(searchTerm); - - organizationGrid.getChildren().addAll(updated.getChildren()); - organizationGrid.getColumnConstraints().addAll(updated.getColumnConstraints()); + + int column = 0; + int row = 0; + + for (Organization org : filtered.values()) { + OrganizationCard card = cardCache.get(org.orgNumber()); + if (card != null) { + organizationGrid.add(card, column, row); + column++; + if (column == 4) { + column = 0; + row++; + } + } + } + + for (int i = 0; i < 4; i++) { + ColumnConstraints col = new ColumnConstraints(); + col.setPercentWidth(25); + organizationGrid.getColumnConstraints().add(col); + } } } diff --git a/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java b/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java index 2ca30bb..483d0e7 100644 --- a/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java +++ b/src/main/java/edu/group5/app/view/causespage/OrganizationCard.java @@ -1,7 +1,7 @@ package edu.group5.app.view.causespage; import edu.group5.app.control.NavigationController; -import edu.group5.app.model.AppState; +import edu.group5.app.control.OrganizationController; import edu.group5.app.model.organization.Organization; import javafx.geometry.Pos; import javafx.scene.image.Image; @@ -21,15 +21,16 @@ * to the organization's detail page.

    */ public class OrganizationCard extends VBox { - private final AppState appState; private final Organization organization; private final NavigationController nav; + private final OrganizationController organizationController; private StackPane imageContainer; private String currentLogoUrl; - public OrganizationCard(AppState appstate, NavigationController nav, Organization org, String img) { - this.appState = appstate; + public OrganizationCard(NavigationController nav, OrganizationController organizationController, + Organization org, String img) { this.nav = nav; + this.organizationController = organizationController; this.organization = org; setId("mainContainer"); getStylesheets().add(getClass().getResource("/browsepage/browse_org.css").toExternalForm()); @@ -42,7 +43,7 @@ public OrganizationCard(AppState appstate, NavigationController nav, Organizatio ); setOnMouseClicked(e -> { - appstate.setCurrentOrganization(getOrganizationWithCurrentLogo()); + organizationController.setCurrentOrganization(getOrganizationWithCurrentLogo()); nav.showOrganizationPage(); }); @@ -60,7 +61,7 @@ public void updateLogo(String logoUrl) { if (imageContainer == null) return; imageContainer.getChildren().clear(); if (logoUrl != null && !logoUrl.isBlank()) { - ImageView logo = new ImageView(new Image(logoUrl, true)); + ImageView logo = new ImageView(new Image(logoUrl, 80, 80, true, true, true)); logo.setId("logo"); logo.setSmooth(true); logo.setPreserveRatio(true); @@ -95,7 +96,7 @@ private StackPane createImageContainer(String img) { if (img != null && !img.isBlank()) { - ImageView logo = new ImageView(new Image(img, true)); + ImageView logo = new ImageView(new Image(img, 80, 80, true, true, true)); logo.setId("logo"); logo.setSmooth(true); logo.setPreserveRatio(true); diff --git a/src/main/java/edu/group5/app/view/donationpage/DonationPageView.java b/src/main/java/edu/group5/app/view/donationpage/DonationPageView.java index 9444421..ed0288c 100644 --- a/src/main/java/edu/group5/app/view/donationpage/DonationPageView.java +++ b/src/main/java/edu/group5/app/view/donationpage/DonationPageView.java @@ -2,7 +2,7 @@ import edu.group5.app.control.DonationController; import edu.group5.app.control.NavigationController; -import edu.group5.app.model.AppState; +import edu.group5.app.utils.ParameterValidator; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.TextField; @@ -27,7 +27,6 @@ * payment method buttons, donation button, and a back to organization page button.

    */ public class DonationPageView extends BorderPane { - private final AppState appState; private final NavigationController nav; private final DonationController donationController; @@ -36,8 +35,10 @@ public class DonationPageView extends BorderPane { private Node selectedPaymentMethod = null; private Button donateBtn; - public DonationPageView(AppState appState, NavigationController nav, DonationController donationController) { - this.appState = appState; + public DonationPageView(NavigationController nav, DonationController donationController) { + ParameterValidator.objectChecker(nav, "NavigationController"); + ParameterValidator.objectChecker(donationController, "DonationController"); + this.nav = nav; this.donationController = donationController; @@ -214,7 +215,7 @@ private void selectPaymentMethod(Node element) { selectedPaymentMethod.getStyleClass().add("payment-method-selected"); String paymentMethod = (String) element.getUserData(); - appState.setCurrentPaymentMethod(paymentMethod); + donationController.setPaymentMethod(paymentMethod); updateDonationButtonState(); } @@ -238,7 +239,7 @@ private void clearSelection() { } private void updateDonationAmount(BigDecimal amount) { - appState.setCurrentDonationAmount(amount); + donationController.setDonationAmount(amount); } private BigDecimal parseAmount(String amountStr) { diff --git a/src/main/java/edu/group5/app/view/homepage/HomePageView.java b/src/main/java/edu/group5/app/view/homepage/HomePageView.java index c24b89b..137e0b1 100644 --- a/src/main/java/edu/group5/app/view/homepage/HomePageView.java +++ b/src/main/java/edu/group5/app/view/homepage/HomePageView.java @@ -1,7 +1,6 @@ package edu.group5.app.view.homepage; import edu.group5.app.control.NavigationController; -import edu.group5.app.model.AppState; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.ScrollPane; @@ -19,11 +18,9 @@ * and an about us button. The page also has a charity image at the bottom.

    */ public class HomePageView extends BorderPane { - private final AppState appState; private final NavigationController nav; - public HomePageView(AppState appState, NavigationController nav) { - this.appState = appState; + public HomePageView(NavigationController nav) { this.nav = nav; getStylesheets().add(getClass().getResource("/homepage/homepage.css").toExternalForm()); diff --git a/src/main/java/edu/group5/app/view/loginpage/LoginPageView.java b/src/main/java/edu/group5/app/view/loginpage/LoginPageView.java index 11b3ba7..927a8af 100644 --- a/src/main/java/edu/group5/app/view/loginpage/LoginPageView.java +++ b/src/main/java/edu/group5/app/view/loginpage/LoginPageView.java @@ -2,7 +2,6 @@ import edu.group5.app.control.NavigationController; import edu.group5.app.control.AuthController; -import edu.group5.app.model.AppState; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.Label; @@ -21,7 +20,6 @@ * a login box, an email box, a login button, and a register button.

    */ public class LoginPageView extends BorderPane { - private final AppState appState; private final NavigationController nav; private final AuthController authController; @@ -29,8 +27,7 @@ public class LoginPageView extends BorderPane { private PasswordField passwordField; private Label errorLabel; - public LoginPageView(AppState appState, NavigationController nav, AuthController authController) { - this.appState = appState; + public LoginPageView(NavigationController nav, AuthController authController) { this.nav = nav; this.authController = authController; diff --git a/src/main/java/edu/group5/app/view/loginpage/SignUpPageView.java b/src/main/java/edu/group5/app/view/loginpage/SignUpPageView.java index 8638a1f..bdcfb54 100644 --- a/src/main/java/edu/group5/app/view/loginpage/SignUpPageView.java +++ b/src/main/java/edu/group5/app/view/loginpage/SignUpPageView.java @@ -2,7 +2,6 @@ import edu.group5.app.control.NavigationController; import edu.group5.app.control.AuthController; -import edu.group5.app.model.AppState; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.Label; @@ -22,7 +21,6 @@ * a password field, a sign up button, and an back to login button.

    */ public class SignUpPageView extends BorderPane { - private final AppState appState; private final NavigationController nav; private final AuthController authController; @@ -32,8 +30,7 @@ public class SignUpPageView extends BorderPane { private PasswordField passwordField; private Label errorLabel; - public SignUpPageView(AppState appState, NavigationController nav, AuthController authController) { - this.appState = appState; + public SignUpPageView(NavigationController nav, AuthController authController) { this.nav = nav; this.authController = authController; diff --git a/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java b/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java index 53c623b..f591d26 100644 --- a/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java +++ b/src/main/java/edu/group5/app/view/organizationpage/OrganizationPageView.java @@ -2,14 +2,14 @@ import edu.group5.app.control.DonationController; import edu.group5.app.control.NavigationController; -import edu.group5.app.model.AppState; +import edu.group5.app.control.OrganizationController; import edu.group5.app.model.organization.Organization; +import javafx.application.Platform; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; -import javafx.scene.control.TextArea; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; @@ -30,16 +30,21 @@ * donation page, and a back button to return to the causes page.

    */ public class OrganizationPageView extends BorderPane { - private final AppState appState; private final NavigationController nav; + private final OrganizationController organizationController; private final DonationController donationController; - public OrganizationPageView(AppState appState, NavigationController nav, DonationController donationController) { - this.appState = appState; + public OrganizationPageView(NavigationController nav, + OrganizationController organizationController, + DonationController donationController) { this.nav = nav; + this.organizationController = organizationController; this.donationController = donationController; - getStylesheets().add(Objects.requireNonNull(getClass().getResource("/organizationpage/organizationpage.css")).toExternalForm()); + getStylesheets().add(Objects + .requireNonNull(getClass() + .getResource("/organizationpage/organizationpage.css")) + .toExternalForm()); VBox content = new VBox(); content.getChildren().addAll(createBackButton(), createBody()); @@ -85,29 +90,44 @@ private StackPane createImageContainer() { imageContainer.setPrefWidth(120); imageContainer.setMaxWidth(120); - Organization org = appState.getCurrentOrganization(); + Organization org = organizationController.getCurrentOrganization(); + if (org != null && org.logoUrl() != null && !org.logoUrl().isBlank()) { - ImageView logo = new ImageView(new Image(org.logoUrl(), true)); - logo.setId("logo"); - logo.setSmooth(true); - logo.setPreserveRatio(true); - logo.setFitHeight(350); - logo.setFitWidth(350); - imageContainer.getChildren().add(logo); + // Load image in background thread to avoid blocking UI + new Thread(() -> { + try { + Image image = new Image(org.logoUrl(), 120, 120, true, true); + Platform.runLater(() -> { + ImageView logo = new ImageView(image); + logo.setId("logo"); + logo.setSmooth(true); + logo.setPreserveRatio(true); + logo.setFitHeight(350); + logo.setFitWidth(350); + imageContainer.getChildren().clear(); + imageContainer.getChildren().add(logo); + }); + } catch (Exception e) { + // Logo failed to load, show placeholder + Platform.runLater(() -> { + imageContainer.getChildren().clear(); + Text text = new Text("No image"); + text.setStyle("-fx-font-size: 10;"); + imageContainer.getChildren().add(text); + }); + } + }, "LogoLoader").start(); } else { - StackPane placeholder = new StackPane(); - Text text = new Text("No image"); text.setStyle("-fx-font-size: 10;"); - - placeholder.getChildren().add(text); - imageContainer.getChildren().add(placeholder); + imageContainer.getChildren().add(text); } + return imageContainer; } private VBox createOrgInfoSection() { - Organization org = appState.getCurrentOrganization(); + Organization org = organizationController.getCurrentOrganization(); VBox orgInfoSection = new VBox(); orgInfoSection.setSpacing(50); diff --git a/src/main/java/edu/group5/app/view/userpage/UserPageView.java b/src/main/java/edu/group5/app/view/userpage/UserPageView.java index 5e2370a..cf6f801 100644 --- a/src/main/java/edu/group5/app/view/userpage/UserPageView.java +++ b/src/main/java/edu/group5/app/view/userpage/UserPageView.java @@ -4,7 +4,6 @@ import edu.group5.app.control.NavigationController; import edu.group5.app.control.OrganizationController; import edu.group5.app.control.AuthController; -import edu.group5.app.model.AppState; import edu.group5.app.model.donation.Donation; import edu.group5.app.model.organization.Organization; import edu.group5.app.model.user.User; @@ -19,24 +18,20 @@ import javafx.scene.layout.BorderPane; import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; -import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; import javafx.scene.text.Text; import java.text.SimpleDateFormat; - -import java.math.RoundingMode; import java.util.*; public class UserPageView extends BorderPane { - private final AppState appState; private final NavigationController nav; private final AuthController authController; private final DonationController donationController; private final OrganizationController organizationController; - public UserPageView(AppState appState, NavigationController nav, AuthController authController, DonationController donationController, OrganizationController organizationController) { - this.appState = appState; + public UserPageView(NavigationController nav, AuthController authController, + DonationController donationController, OrganizationController organizationController) { this.nav = nav; this.authController = authController; this.donationController = donationController; @@ -51,13 +46,15 @@ public UserPageView(AppState appState, NavigationController nav, AuthController } private HBox createProfileSection() { - ImageView avatar = new ImageView(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/userpage/account_circle.png")))); + ImageView avatar = new ImageView(new Image(Objects + .requireNonNull(getClass() + .getResourceAsStream("/userpage/account_circle.png")))); avatar.setFitWidth(150); avatar.setFitHeight(150); avatar.setPreserveRatio(true); avatar.setId("avatar"); - User currentUser = appState.getCurrentUser(); + User currentUser = authController.getCurrentUser(); Text name = new Text(currentUser.getFirstName() + " " + currentUser.getLastName()); name.setId("profile-name"); @@ -133,7 +130,7 @@ private VBox createDonationsSection() { donationsBox.getStyleClass().add("donation-list"); donationsBox.setPadding(new Insets(10)); - User currentUser = appState.getCurrentUser(); + User currentUser = authController.getCurrentUser(); Map userDonations = donationController.getUserDonations(currentUser.getUserId()); 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 ce81b2a..82140a7 100644 --- a/src/test/java/edu/group5/app/model/donation/DonationRepositoryTest.java +++ b/src/test/java/edu/group5/app/model/donation/DonationRepositoryTest.java @@ -33,7 +33,7 @@ void setUp() { void constructorThrowsIfNullList() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> new DonationRepository(null)); - assertEquals("The list of rows cannot be null", ex.getMessage()); + assertEquals("List of donation rows can't be null", ex.getMessage()); } @Test @@ -88,7 +88,7 @@ void addContentDuplicateIdFails() { void addContentNullThrows() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> repo.addContent(null)); - assertEquals("Donation cannot be null", ex.getMessage()); + assertEquals("Donation can't be null", ex.getMessage()); } @Test @@ -102,7 +102,7 @@ void getDonationByIdSuccessfully() { void getDonationByIdThrowsIfNegative() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> repo.getDonationById(0)); - assertEquals("Donation ID must be positive", ex.getMessage()); + assertEquals("Donation ID must be a positive integer", ex.getMessage()); } @Test @@ -182,7 +182,7 @@ void filterByOrganizationNoMatch() { void filterByOrganizationThrowsIfNegative() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> repo.filterByOrganization(0)); - assertEquals("Organization number must be positive", ex.getMessage()); + assertEquals("Organization number must be a positive integer", ex.getMessage()); } @Test @@ -261,7 +261,7 @@ void filterByUserIdNoMatch() { void filterByUserIdThrowsIfNegative() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> repo.filterByUser(0)); - assertEquals("User ID must be positive", ex.getMessage()); + assertEquals("User ID must be a positive integer", ex.getMessage()); } @Test diff --git a/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java b/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java index 3ed1aec..58bd170 100644 --- a/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java +++ b/src/test/java/edu/group5/app/model/donation/DonationServiceTest.java @@ -50,7 +50,7 @@ void testConstructorThrowsIfDonationRepositoryIsNull() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { new DonationService(null, organizationRepository); }); - assertEquals("DonationRepository cannot be null", exception.getMessage()); + assertEquals("DonationRepository can't be null", exception.getMessage()); } @Test @@ -58,17 +58,7 @@ void testConstructorThrowsIfOrganizationRepositoryIsNull() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { new DonationService(donationRepository, null); }); - assertEquals("OrganizationRepository cannot be null", exception.getMessage()); - } - - @Test - void testGetDonationRepository() { - assertEquals(donationRepository, donationService.getDonationRepository()); - } - - @Test - void testGetOrganizationRepository() { - assertEquals(organizationRepository, donationService.getOrganizationRepository()); + assertEquals("OrganizationRepository can't be null", exception.getMessage()); } @Test 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 8906395..a5eab05 100644 --- a/src/test/java/edu/group5/app/model/organization/OrganizationRepositoryTest.java +++ b/src/test/java/edu/group5/app/model/organization/OrganizationRepositoryTest.java @@ -1,157 +1,157 @@ -package edu.group5.app.model.organization; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.*; - -class OrganizationRepositoryTest { - - private OrganizationRepository repository; - private OrganizationScraper scraper; - - @BeforeEach - void setUp() { - scraper = new OrganizationScraper(); - Object[] content = new Object[] { - Map.of( - "org_number", "1", - "name", "Trusted Org1", - "status", "approved", - "url", "org.com", - "is_pre_approved", true - ), - Map.of( - "org_number", "2", - "name", "Trusted Org2", - "status", "approved", - "url", "org.com", - "is_pre_approved", true - ), - Map.of( - "org_number", "3", - "name", "Untrusted Org1", - "status", "pending", - "url", "org.com", - "is_pre_approved", true - ), - Map.of( - "org_number", "4", - "name", "Untrusted Org2", - "status", "pending", - "url", "org.com", - "is_pre_approved", true - ) - }; - repository = new OrganizationRepository(content, scraper); - } - - private void constructorTest(Object[] input, String expectedMessage) { - IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> new OrganizationRepository(input, scraper) - ); - assertEquals(expectedMessage, exception.getMessage()); - } - @Test - void constructor_ThrowsWhenContentIsNull() { - constructorTest(null, "The input cannot be null"); - } - - @Test - void constructor_ThrowsWhenScraperIsNull() { - Object[] content = new Object[] { - Map.of( - "org_number", "1", - "name", "Org", - "status", "approved", - "url", "org.com", - "is_pre_approved", true - ) - }; - IllegalArgumentException exception = assertThrows( - IllegalArgumentException.class, - () -> new OrganizationRepository(content, null) - ); - assertEquals("The scraper cannot be null", exception.getMessage()); - } - - @Test - void getTrustedOrganizations_OnlyReturnsTrustedOrganizations() { - Map trusted = repository.getTrustedOrganizations(); - - assertEquals(2, trusted.size()); - assertTrue(trusted.containsKey(1)); - assertTrue(trusted.containsKey(2)); - assertFalse(trusted.containsKey(3)); - 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", null), - 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", null), - 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", null), - 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], scraper); - IllegalStateException exception = assertThrows( - IllegalStateException.class, () -> emptyRepo.export() - ); - assertEquals("The repository is empty", exception.getMessage()); - } +package edu.group5.app.model.organization; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +class OrganizationRepositoryTest { + + private OrganizationRepository repository; + private OrganizationScraper scraper; + + @BeforeEach + void setUp() { + scraper = new OrganizationScraper(); + Object[] content = new Object[] { + Map.of( + "org_number", "1", + "name", "Trusted Org1", + "status", "approved", + "url", "org.com", + "is_pre_approved", true + ), + Map.of( + "org_number", "2", + "name", "Trusted Org2", + "status", "approved", + "url", "org.com", + "is_pre_approved", true + ), + Map.of( + "org_number", "3", + "name", "Untrusted Org1", + "status", "pending", + "url", "org.com", + "is_pre_approved", true + ), + Map.of( + "org_number", "4", + "name", "Untrusted Org2", + "status", "pending", + "url", "org.com", + "is_pre_approved", true + ) + }; + repository = new OrganizationRepository(content, scraper); + } + + private void constructorTest(Object[] input, String expectedMessage) { + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> new OrganizationRepository(input, scraper) + ); + assertEquals(expectedMessage, exception.getMessage()); + } + @Test + void constructor_ThrowsWhenContentIsNull() { + constructorTest(null, "Input data can't be null"); + } + + @Test + void constructor_ThrowsWhenScraperIsNull() { + Object[] content = new Object[] { + Map.of( + "org_number", "1", + "name", "Org", + "status", "approved", + "url", "org.com", + "is_pre_approved", true + ) + }; + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> new OrganizationRepository(content, null) + ); + assertEquals("Scraper can't be null", exception.getMessage()); + } + + @Test + void getTrustedOrganizations_OnlyReturnsTrustedOrganizations() { + Map trusted = repository.getTrustedOrganizations(); + + assertEquals(2, trusted.size()); + assertTrue(trusted.containsKey(1)); + assertTrue(trusted.containsKey(2)); + assertFalse(trusted.containsKey(3)); + 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", null), + repository.findByOrgNumber(1)); + } + + @Test + void testFindByOrgNumberIfOrgNumberIsIllegal() { + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> repository.findByOrgNumber(-1)); + assertEquals("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", null), + repository.findByOrgName("Trusted Org1")); + } + + @Test + void testFindByOrgNameIfNameIsIllegalThrowsException() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> repository.findByOrgName(null)); + assertEquals("Organization name can't be null", exception.getMessage()); + + IllegalArgumentException exception2 = assertThrows(IllegalArgumentException.class, + () -> repository.findByOrgName("")); + assertEquals("Organization name can't be blank", 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", null), + 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], scraper); + 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/organization/OrganizationServiceTest.java b/src/test/java/edu/group5/app/model/organization/OrganizationServiceTest.java index 2d76391..3765bf5 100644 --- a/src/test/java/edu/group5/app/model/organization/OrganizationServiceTest.java +++ b/src/test/java/edu/group5/app/model/organization/OrganizationServiceTest.java @@ -33,19 +33,14 @@ public void setUp() { void constructor_throwsIfRepositoryIsNull() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> new OrganizationService(null, scraper)); - assertEquals("OrganizationRepository cannot be null", ex.getMessage()); + assertEquals("OrganizationRepository can't be null", ex.getMessage()); } @Test void constructor_throwsIfScraperIsNull() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> new OrganizationService(repo, null)); - assertEquals("OrganizationScraper cannot be null", ex.getMessage()); - } - - @Test - void testGetOrganizationRepository() { - assertEquals(repo, service.getOrganizationRepository()); + assertEquals("Scraper can't be null", ex.getMessage()); } @Test 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 7c96438..147343f 100644 --- a/src/test/java/edu/group5/app/model/user/CustomerTest.java +++ b/src/test/java/edu/group5/app/model/user/CustomerTest.java @@ -59,55 +59,55 @@ void testInstanceOfCustomer() { @Test void constructorWithNegativeUserIdThrowsException() { constructorTest(-1, testFirstName, testLastName, - testEmail, testPasswordHash, "User ID must be positive"); + testEmail, testPasswordHash, "User ID must be a positive integer"); } @Test void constructorWithNullFirstNameThrowsException() { constructorTest(testUserId, null, testLastName, - testEmail, testPasswordHash, "First name cannot be null or empty"); + testEmail, testPasswordHash, "First name can't be null"); } @Test void constructorWithEmptyFirstNameThrowsException() { constructorTest(testUserId, "", testLastName, - testEmail, testPasswordHash, "First name cannot be null or empty"); + testEmail, testPasswordHash, "First name can't be blank"); } @Test void constructorWithNullLastNameThrowsException() { constructorTest(testUserId, testFirstName, null, - testEmail, testPasswordHash, "Last name cannot be null or empty"); + testEmail, testPasswordHash, "Last name can't be null"); } @Test void constructorWithEmptyLastNameThrowsException() { - constructorTest(testUserId, testFirstName, - "", testEmail, testPasswordHash, "Last name cannot be null or empty"); + constructorTest(testUserId, testFirstName, + "", testEmail, testPasswordHash, "Last name can't be blank"); } @Test void constructorWithNullEmailThrowsException() { constructorTest(testUserId, testFirstName, testLastName, - null, testPasswordHash, "Email cannot be null or empty"); + null, testPasswordHash, "Email can't be null"); } @Test void constructorWithEmptyEmailThrowsException() { constructorTest(testUserId, testFirstName, testLastName, - "", testPasswordHash, "Email cannot be null or empty"); + "", testPasswordHash, "Email can't be blank"); } @Test void constructorWithNullPasswordHashThrowsException() { constructorTest(testUserId, testFirstName, testLastName, - testEmail, null, "Password hash cannot be null or empty"); + testEmail, null, "Password hash can't be null"); } @Test void constructorWithEmptyPasswordHashThrowsException() { constructorTest(testUserId, testFirstName, testLastName, - testEmail, "", "Password hash cannot be null or empty"); + testEmail, "", "Password hash can't be blank"); } diff --git a/src/test/java/edu/group5/app/model/user/UserRepositoryTest.java b/src/test/java/edu/group5/app/model/user/UserRepositoryTest.java index eaf2c2c..fd13b13 100644 --- a/src/test/java/edu/group5/app/model/user/UserRepositoryTest.java +++ b/src/test/java/edu/group5/app/model/user/UserRepositoryTest.java @@ -27,7 +27,7 @@ void setUp() { void constructorThrowsIfNull() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> new UserRepository(null)); - assertEquals("The list of rows cannot be null", ex.getMessage()); + assertEquals("List of User rows can't be null", ex.getMessage()); } @Test @@ -77,7 +77,7 @@ void addContentSuccessfully() { void addContentNullThrows() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> repo.addContent(null)); - assertEquals("User cannot be null", ex.getMessage()); + assertEquals("User can't be null", ex.getMessage()); } @Test @@ -101,11 +101,11 @@ void findUserByEmailReturnsNullIfNotFound() { void findUserByEmailThrowsIfNullOrEmpty() { IllegalArgumentException ex1 = assertThrows(IllegalArgumentException.class, () -> repo.findUserByEmail(null)); - assertEquals("Email cannot be null or empty", ex1.getMessage()); + assertEquals("Email can't be null", ex1.getMessage()); IllegalArgumentException ex2 = assertThrows(IllegalArgumentException.class, () -> repo.findUserByEmail(" ")); - assertEquals("Email cannot be null or empty", ex2.getMessage()); + assertEquals("Email can't be blank", ex2.getMessage()); } @Test @@ -123,7 +123,7 @@ void getUserByIdReturnsNullIfNotFound() { void getUserByIdThrowsIfNonPositive() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> repo.getUserById(0)); - assertEquals("User ID must be positive", ex.getMessage()); + assertEquals("User ID must be a positive integer", ex.getMessage()); } diff --git a/src/test/java/edu/group5/app/model/user/UserServiceTest.java b/src/test/java/edu/group5/app/model/user/UserServiceTest.java index acec10b..104e2f1 100644 --- a/src/test/java/edu/group5/app/model/user/UserServiceTest.java +++ b/src/test/java/edu/group5/app/model/user/UserServiceTest.java @@ -31,12 +31,7 @@ void setUp() { void constructorthrowsIfNull() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> new UserService(null)); - assertEquals("UserRepository cannot be null", ex.getMessage()); - } - - @Test - void testGetUserRepository() { - assertEquals(repo, service.getUserRepository()); + assertEquals("UserRepository can't be null", ex.getMessage()); } @Test