From 37089010fb30276c315db33e213ec96c75cf88ec Mon Sep 17 00:00:00 2001 From: Marius Klepp Date: Thu, 16 Apr 2026 09:46:32 +0200 Subject: [PATCH 1/3] Javadoc for UI classes --- src/main/java/ui/DonationSession.java | 47 ++++++++++------------ src/main/java/ui/DonationSessionAware.java | 10 +++++ src/main/java/ui/Page.java | 10 ++++- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/main/java/ui/DonationSession.java b/src/main/java/ui/DonationSession.java index 991f91a..0d3bf1d 100644 --- a/src/main/java/ui/DonationSession.java +++ b/src/main/java/ui/DonationSession.java @@ -2,51 +2,46 @@ import domain.organization.Organization; +/** + * Holds the state of an ongoing donation session. + * Passed between donation flow controllers to carry + * the selected organization and amount. + */ public class DonationSession { private Organization organization; private int amount; - private String name; - private String email; - private String phone; + /** + * Returns the organization selected for donation. + * @return the selected organization + */ public Organization getOrganization() { return organization; } + /** + * Returns the donation amount. + * @return the amount in NOK + */ public int getAmount() { return amount; } - public String getName() { - return name; - } - - public String getEmail() { - return email; - } - - public String getPhone() { - return phone; - } - + /** + * Sets the organization to donate to. + * @param organization the organization to set + */ public void setOrganization(Organization organization) { this.organization = organization; } + /** + * Sets the donation amount. + * @param amount the amount in NOK + */ public void setAmount(int amount) { this.amount = amount; } - public void setName(String name) { - this.name = name; - } - - public void setEmail(String email) { - this.email = email; - } - - public void setPhone(String phone) { - this.phone = phone; - } } diff --git a/src/main/java/ui/DonationSessionAware.java b/src/main/java/ui/DonationSessionAware.java index 8ceab22..75b9965 100644 --- a/src/main/java/ui/DonationSessionAware.java +++ b/src/main/java/ui/DonationSessionAware.java @@ -1,5 +1,15 @@ package ui; +/** + * Interface for controllers that participate in the donation flow. + * Implementing controllers receive the current {@link DonationSession} + * when they are loaded. + */ public interface DonationSessionAware { + + /** + * Sets the current donation session. + * @param session the active donation session + */ void setDonationSession(DonationSession session); } diff --git a/src/main/java/ui/Page.java b/src/main/java/ui/Page.java index 7dbf0e0..77cac0f 100644 --- a/src/main/java/ui/Page.java +++ b/src/main/java/ui/Page.java @@ -16,14 +16,20 @@ public enum Page { DONATION_PAYMENT("DonationPayment.fxml"), DONATION_CONFIRMATION("DonationConfirmation.fxml"); - - private final String fileName; + /** + * Constructs a Page with the associated FXML filename. + * @param fileName the name of the FXML file for this page + */ Page(String fileName) { this.fileName = fileName; } + /** + * Returns the FXML filename associated with this page. + * @return the FXML filename + */ public String getFileName() { return fileName; } From e22cc28c2422744b5b4809503f17e5dbfd4c503c Mon Sep 17 00:00:00 2001 From: Marius Klepp Date: Thu, 16 Apr 2026 14:13:22 +0200 Subject: [PATCH 2/3] Documentation: Added javadoc and removed unused method and files --- src/main/java/app/Main.java | 6 +- .../java/application/user/UserDonate.java | 16 ++++- .../java/application/user/UserRegister.java | 7 +-- .../java/application/user/UserSignIn.java | 7 +-- .../java/application/user/UserStatistics.java | 16 ++--- src/main/java/domain/donation/Cause.java | 6 -- src/main/java/domain/donation/Donation.java | 3 +- .../domain/organization/Organization.java | 59 +++++++++++++++++-- .../organization/OrganizationDetails.java | 21 ++++++- src/main/java/domain/user/Role.java | 7 --- src/main/java/domain/user/User.java | 45 +++----------- .../InnsamlingskontrollenClient.java | 15 ++++- .../security/Sha256PasswordHasher.java | 4 +- src/main/java/persistence/CauseDao.java | 4 -- .../persistence/{ => dao}/DonationDao.java | 2 +- .../{ => dao}/OrganizationDao.java | 2 +- .../java/persistence/{ => dao}/UserDao.java | 13 +--- .../persistence/{ => dao}/UserRepository.java | 3 +- .../java/ui/controller/HomeController.java | 14 ++--- .../java/ui/controller/MainController.java | 30 +++++++--- .../ui/controller/MyProfileController.java | 10 +++- .../java/ui/controller/NavbarController.java | 38 +++++++++--- .../ui/controller/OrganizationController.java | 21 +++++-- .../OrganizationDetailController.java | 21 ++++++- .../ui/controller/RegisterController.java | 35 +++++++---- .../java/ui/controller/SignInController.java | 24 ++++---- .../donation/DonationAmountController.java | 19 ++++++ .../DonationConfirmationController.java | 13 +++- .../donation/DonationFlowController.java | 25 ++++++++ .../DonationNotLoggedInController.java | 16 ++++- .../donation/DonationPaymentController.java | 21 ++++++- src/main/java/util/SessionManager.java | 2 +- 32 files changed, 356 insertions(+), 169 deletions(-) delete mode 100644 src/main/java/domain/donation/Cause.java delete mode 100644 src/main/java/domain/user/Role.java delete mode 100644 src/main/java/persistence/CauseDao.java rename src/main/java/persistence/{ => dao}/DonationDao.java (99%) rename src/main/java/persistence/{ => dao}/OrganizationDao.java (98%) rename src/main/java/persistence/{ => dao}/UserDao.java (94%) rename src/main/java/persistence/{ => dao}/UserRepository.java (95%) diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java index fea73ff..8c34abd 100644 --- a/src/main/java/app/Main.java +++ b/src/main/java/app/Main.java @@ -10,9 +10,9 @@ import java.math.BigDecimal; import java.sql.SQLException; -import persistence.DonationDao; -import persistence.OrganizationDao; -import persistence.UserDao; +import persistence.dao.DonationDao; +import persistence.dao.OrganizationDao; +import persistence.dao.UserDao; import persistence.db.Database; import javafx.application.Application; import javafx.fxml.FXMLLoader; diff --git a/src/main/java/application/user/UserDonate.java b/src/main/java/application/user/UserDonate.java index bf8f187..bf3889f 100644 --- a/src/main/java/application/user/UserDonate.java +++ b/src/main/java/application/user/UserDonate.java @@ -3,17 +3,29 @@ import domain.donation.Donation; import domain.organization.Organization; import domain.user.User; -import persistence.DonationDao; - +import persistence.dao.DonationDao; import java.math.BigDecimal; +/** + * Use case for handling donations made by a user to an organization. + * Creates a {@link Donation} and persists it to the database. + */ public class UserDonate { private final DonationDao donationDao; + /** + * Creates a new {@code UserDonate} with a default {@link DonationDao}. + */ public UserDonate() { this.donationDao = new DonationDao(); } + /** + * Executes the donation use case by creating and saving a donation. + * @param user the user making the donation + * @param org the organization receiving the donation + * @param amount the donation amount in NOK + */ public void execute(User user, Organization org, BigDecimal amount) { Donation donation = new Donation(amount, user, org); donationDao.insert(donation); diff --git a/src/main/java/application/user/UserRegister.java b/src/main/java/application/user/UserRegister.java index adc2107..0a07316 100644 --- a/src/main/java/application/user/UserRegister.java +++ b/src/main/java/application/user/UserRegister.java @@ -2,7 +2,7 @@ import application.security.PasswordHasher; import domain.user.User; -import persistence.UserRepository; +import persistence.dao.UserRepository; /** * The UserRegister class is responsible for handling the user registration process. @@ -43,7 +43,6 @@ public void execute(String username, String phoneNumber, String password, String email = email.trim().toLowerCase(); phoneNumber = phoneNumber.trim(); - // duplicate check (repo.existsByEmail) if (repo.existsByEmail(email)) { throw new IllegalArgumentException("Email already in use"); } @@ -51,13 +50,9 @@ public void execute(String username, String phoneNumber, String password, String throw new IllegalArgumentException("This username is taken"); } - // hash password (optional) String hashedPassword = hasher.hash(password); - // new User(...) (domain validates) User user = new User(username, phoneNumber, hashedPassword, email); - - // saves user repo.insert(user); } } diff --git a/src/main/java/application/user/UserSignIn.java b/src/main/java/application/user/UserSignIn.java index be6b159..f7ea596 100644 --- a/src/main/java/application/user/UserSignIn.java +++ b/src/main/java/application/user/UserSignIn.java @@ -2,7 +2,7 @@ import application.security.PasswordHasher; import domain.user.User; -import persistence.UserRepository; +import persistence.dao.UserRepository; /** * The UserSignIn class handles the process of user authentication. @@ -36,22 +36,17 @@ public UserSignIn(PasswordHasher hasher, UserRepository repo) { * @return user object, if it exists. */ public User execute(String login, String password) { - // normalize (trim etc.) login.trim().toLowerCase(); User user = repo.findByLogin(login) .orElseThrow(() -> new IllegalArgumentException("Invalid credentials")); - // hash password (optional) String hashedInput = hasher.hash(password); - // compare hashed input with stored hash if (!hashedInput.equals(user.getPassword())) { throw new IllegalArgumentException("Invalid password"); } - System.out.println("Loggin in"); - // return user (if match) return user; } } diff --git a/src/main/java/application/user/UserStatistics.java b/src/main/java/application/user/UserStatistics.java index ed0f2dc..4d3b960 100644 --- a/src/main/java/application/user/UserStatistics.java +++ b/src/main/java/application/user/UserStatistics.java @@ -1,9 +1,9 @@ package application.user; import domain.user.User; -import java.util.ArrayList; + import java.util.List; -import persistence.DonationDao; +import persistence.dao.DonationDao; /** * Provides statistical information about a user's donation activity. @@ -28,8 +28,7 @@ public UserStatistics() { * and its donation count. Returns empty list if the user has no donations. */ public List userFavoriteOrganization(User user) { - List favoriteOrganization = donationDao.getFavoriteOrganization(user.getID()); - return favoriteOrganization; + return donationDao.getFavoriteOrganization(user.getID()); } /** @@ -40,8 +39,7 @@ public List userFavoriteOrganization(User user) { * @return a list of formatted donation strings. Returns empty list if the user has no donations */ public List userDonations(User user) { - List userDonationList = donationDao.getUserDonations(user.getID()); - return userDonationList; + return donationDao.getUserDonations(user.getID()); } /** @@ -51,8 +49,7 @@ public List userDonations(User user) { * @return a string representation of the total donated amount. Returns "0" if no donations exist */ public String userTotalDonationAmount(User user) { - String totalDonations = donationDao.getTotalDonationAmount(user.getID()); - return totalDonations; + return donationDao.getTotalDonationAmount(user.getID()); } /** @@ -62,7 +59,6 @@ public String userTotalDonationAmount(User user) { * @return a string representation of the total number of donations. Returns "0" if none exist. */ public String getTotalDonationsMade(User user) { - String totalDonationsMade = donationDao.getTotalDonationsMade(user.getID()); - return totalDonationsMade; + return donationDao.getTotalDonationsMade(user.getID()); } } diff --git a/src/main/java/domain/donation/Cause.java b/src/main/java/domain/donation/Cause.java deleted file mode 100644 index a69854e..0000000 --- a/src/main/java/domain/donation/Cause.java +++ /dev/null @@ -1,6 +0,0 @@ -package domain.donation; - -/** - * Enum class that contains 5 different cause types - */ -public enum Cause { HEALTH, EMERGENCY_RELIEF, CHILDREN, ENVIRONMENT, CONFLICTS; } diff --git a/src/main/java/domain/donation/Donation.java b/src/main/java/domain/donation/Donation.java index 9835c00..53e1063 100644 --- a/src/main/java/domain/donation/Donation.java +++ b/src/main/java/domain/donation/Donation.java @@ -12,8 +12,7 @@ * Provides set and get methods. */ public class Donation { - private Long id; //get set to null by default, - // Gets set to a value by database when registered? + private Long id; private final BigDecimal amount; private final LocalDateTime dateTime; private final User user; diff --git a/src/main/java/domain/organization/Organization.java b/src/main/java/domain/organization/Organization.java index f0a470a..2c5ff30 100644 --- a/src/main/java/domain/organization/Organization.java +++ b/src/main/java/domain/organization/Organization.java @@ -2,12 +2,11 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import domain.donation.Cause; -import java.util.Objects; /** - * Class + * Represents a charitable organization retrieved from the Innsamlingskontrollen API. + * Contains basic information such as name, organization number, status and URL. */ @JsonIgnoreProperties(ignoreUnknown = true) public class Organization { @@ -25,23 +24,71 @@ public class Organization { private String url; @JsonProperty("is_pre_approved") - private boolean isPreApproved; + public boolean isPreApproved; - public Organization() {} + public Organization() {} + /** + * Returns the organization number. + * @return the organization number + */ public String getOrgNumber() { return orgNumber; } + + /** + * Sets the organization number. + * @param orgNumber the organization number to set + */ public void setOrgNumber(String orgNumber) { this.orgNumber = orgNumber; } + /** + * Returns the name of the organization. + * @return the organization name + */ public String getName() { return name; } + + /** + * Sets the name of the organization. + * @param name the name to set + */ public void setName(String name) { this.name = name; } + /** + * Returns the approval status of the organization. + * @return the status, either "approved" or "obs" + */ public String getStatus() { return status; } + + /** + * Sets the approval status of the organization. + * @param status the status to set + */ public void setStatus(String status) { this.status = status; } + /** + * Returns the URL to the organization's page on Innsamlingskontrollen. + * @return the URL + */ public String getUrl() { return url; } + + /** + * Sets the URL to the organization's page on Innsamlingskontrollen. + * @param url the URL to set + */ public void setUrl(String url) { this.url = url; } - public boolean isPreApproved() { return isPreApproved; } + /** + * Returns whether the organization is pre-approved. + * @return true if pre-approved, false otherwise + */ + public boolean isPreApproved() { + return isPreApproved; + } + + /** + * Sets whether the organization is pre-approved. + * @param preApproved true if pre-approved, false otherwise + */ public void setPreApproved(boolean preApproved) { isPreApproved = preApproved; } + } diff --git a/src/main/java/domain/organization/OrganizationDetails.java b/src/main/java/domain/organization/OrganizationDetails.java index e892e82..a86920a 100644 --- a/src/main/java/domain/organization/OrganizationDetails.java +++ b/src/main/java/domain/organization/OrganizationDetails.java @@ -1,18 +1,35 @@ package domain.organization; +/** + * Holds additional details about an organization fetched from Innsamlingskontrollen's website. + * Contains a short description and a URL to the organization's logo. + */ public class OrganizationDetails { - private String description; - private String logoUrl; + private final String description; + private final String logoUrl; + /** + * Creates a new OrganizationDetails with the given description and logo URL. + * @param description a short description of the organization + * @param logoUrl the URL to the organization's logo + */ public OrganizationDetails(String description, String logoUrl) { this.description = description; this.logoUrl = logoUrl; } + /** + * Returns the organization's description. + * @return the description + */ public String getDescription() { return description; } + /** + * Returns the URL to the organization's logo. + * @return the logo URL + */ public String getLogoUrl() { return logoUrl; } diff --git a/src/main/java/domain/user/Role.java b/src/main/java/domain/user/Role.java deleted file mode 100644 index 673390f..0000000 --- a/src/main/java/domain/user/Role.java +++ /dev/null @@ -1,7 +0,0 @@ -package domain.user; - -/** - * Enum class that defines if a user has the role Admin or user. - */ -public enum Role {ADMIN, USER -} diff --git a/src/main/java/domain/user/User.java b/src/main/java/domain/user/User.java index e5ff5e9..5d471cc 100644 --- a/src/main/java/domain/user/User.java +++ b/src/main/java/domain/user/User.java @@ -12,12 +12,12 @@ public class User { private Long id; /** - * Constructor that takes in the parameters that define a user. - * It contains input validation on all parameters. - * @param userName - * @param phoneNumber - * @param password - * @param eMail + * Creates a new User with the given parameters. + * Validates all input before assigning. + * @param userName the username; must not be null or blank + * @param phoneNumber the phone number; must be exactly 8 digits + * @param password the hashed password + * @param eMail the email address; must be a valid email format */ public User(String userName, String phoneNumber, String password, String eMail) { @@ -87,33 +87,6 @@ public String getPassword() { */ public long getID() { return id; } - /** - * Sets a new password hash for this user. - * - * @param password the new password hash; must not be null - */ - public void setPassword(String password) { - this.password = password; - } - - /** - * Sets a new username for this user. - * - * @param userName the new username; must not be null or blank - */ - public void setUsername(String userName) { - this.userName = userName; - } - - /** - * Sets a new phone number for this user. - * - * @param phoneNumber the new phone number; must be exactly 8 digits - */ - public void setPhonenumber (String phoneNumber) { - this.phoneNumber = phoneNumber; - } - /** * Sets the ID for this user. * @@ -124,9 +97,9 @@ public void setId(Long id) { } /** - * Method that validates that a e-mail is in the right format. - * @param email - * @return + * Validates that an email address is in the correct format. + * @param email the email address to validate + * @return true if the email is valid, false otherwise */ private static boolean isValidEmail(String email) { if (email == null || email.isBlank()) return false; diff --git a/src/main/java/integration/InnsamlingskontrollenClient.java b/src/main/java/integration/InnsamlingskontrollenClient.java index c6083d9..258f2e9 100644 --- a/src/main/java/integration/InnsamlingskontrollenClient.java +++ b/src/main/java/integration/InnsamlingskontrollenClient.java @@ -20,6 +20,11 @@ */ public class InnsamlingskontrollenClient { + /** + * Fetches all organizations from the Innsamlingskontrollen API. + * @return an array of Organization objects + * @throws Exception if the request fails + */ public static Organization[] fetchOrganizations() throws Exception { HttpClient client = HttpClient.newHttpClient(); @@ -35,18 +40,22 @@ public static Organization[] fetchOrganizations() throws Exception { return mapper.readValue(response.body(), Organization[].class); } + /** + * Fetches additional details for an organization from its Innsamlingskontrollen page. + * Scrapes the organization's description and logo URL using Jsoup. + * Returns empty strings if the details cannot be fetched. + * @param url the URL to the organization's page on Innsamlingskontrollen + * @return an OrganizationDetails object containing description and logo URL + */ public static OrganizationDetails fetchDetails(String url) { try { org.jsoup.Connection conn = Jsoup.connect(url); conn.timeout(10000); Document doc = conn.get(); doc.charset(StandardCharsets.UTF_8); - System.out.println(doc.select("section.information").text()); String description = ""; - System.out.println("Section size: " + doc.select("section.information div > p").size()); for (org.jsoup.nodes.Element p : doc.select("section.information div > p")) { - System.out.println("P element: " + p.text()); if (!p.text().isBlank()) { description = p.text(); break; diff --git a/src/main/java/integration/security/Sha256PasswordHasher.java b/src/main/java/integration/security/Sha256PasswordHasher.java index d6c4a4f..95b760c 100644 --- a/src/main/java/integration/security/Sha256PasswordHasher.java +++ b/src/main/java/integration/security/Sha256PasswordHasher.java @@ -12,9 +12,9 @@ public class Sha256PasswordHasher implements PasswordHasher { /** * Method for hashing an inputted password. * Uses the SHA-256 algorithm. - * Transforms the passoword to a UTF-8 byte-array, then digests it using SHA-256. + * Transforms the password to a UTF-8 byte-array, then digests it using SHA-256. * The bytes are then formatted as a 64-character string. - * @param password + * @param password the password to hash * @return a 64-character hash of the password. * @throws RuntimeException if the hashing fails. */ diff --git a/src/main/java/persistence/CauseDao.java b/src/main/java/persistence/CauseDao.java deleted file mode 100644 index 4ed035d..0000000 --- a/src/main/java/persistence/CauseDao.java +++ /dev/null @@ -1,4 +0,0 @@ -package persistence; - -public class CauseDao { -} diff --git a/src/main/java/persistence/DonationDao.java b/src/main/java/persistence/dao/DonationDao.java similarity index 99% rename from src/main/java/persistence/DonationDao.java rename to src/main/java/persistence/dao/DonationDao.java index 9e70812..e8294ea 100644 --- a/src/main/java/persistence/DonationDao.java +++ b/src/main/java/persistence/dao/DonationDao.java @@ -1,4 +1,4 @@ -package persistence; +package persistence.dao; import domain.donation.Donation; import domain.organization.Organization; diff --git a/src/main/java/persistence/OrganizationDao.java b/src/main/java/persistence/dao/OrganizationDao.java similarity index 98% rename from src/main/java/persistence/OrganizationDao.java rename to src/main/java/persistence/dao/OrganizationDao.java index 470d74a..cd035e1 100644 --- a/src/main/java/persistence/OrganizationDao.java +++ b/src/main/java/persistence/dao/OrganizationDao.java @@ -1,4 +1,4 @@ -package persistence; +package persistence.dao; import domain.organization.Organization; diff --git a/src/main/java/persistence/UserDao.java b/src/main/java/persistence/dao/UserDao.java similarity index 94% rename from src/main/java/persistence/UserDao.java rename to src/main/java/persistence/dao/UserDao.java index 90a13c4..a1b0544 100644 --- a/src/main/java/persistence/UserDao.java +++ b/src/main/java/persistence/dao/UserDao.java @@ -1,8 +1,9 @@ -package persistence; +package persistence.dao; import java.sql.ResultSet; import java.sql.Statement; import java.util.Optional; + import persistence.db.Database; import java.sql.Connection; import java.sql.PreparedStatement; @@ -48,7 +49,6 @@ public void insert(User user) { } } - //private User mapRow(ResultSet rs) throws SQLException {} /** * Finds a user by their email address. @@ -184,7 +184,7 @@ public boolean existsByUsername(String username) { stmt.setString(1, username.trim().toLowerCase()); try (ResultSet rs = stmt.executeQuery()) { - return rs.next(); // true hvis minst én rad finnes + return rs.next(); } } catch (SQLException e) { @@ -192,13 +192,6 @@ public boolean existsByUsername(String username) { } } - //User findById(long id) throws SQLException {} - - //List findAllUsers() throws SQLException {} - - void update(User user) throws SQLException {} - - void updatePassword(long userId, String newPasswordHash) throws SQLException {} private User mapUser(ResultSet rs) throws SQLException { diff --git a/src/main/java/persistence/UserRepository.java b/src/main/java/persistence/dao/UserRepository.java similarity index 95% rename from src/main/java/persistence/UserRepository.java rename to src/main/java/persistence/dao/UserRepository.java index 9b92669..0ee64c6 100644 --- a/src/main/java/persistence/UserRepository.java +++ b/src/main/java/persistence/dao/UserRepository.java @@ -1,7 +1,6 @@ -package persistence; +package persistence.dao; import domain.user.User; - import java.util.Optional; /** diff --git a/src/main/java/ui/controller/HomeController.java b/src/main/java/ui/controller/HomeController.java index 5d5a217..02420a6 100644 --- a/src/main/java/ui/controller/HomeController.java +++ b/src/main/java/ui/controller/HomeController.java @@ -1,20 +1,20 @@ package ui.controller; -import application.security.PasswordHasher; -import application.user.UserSignIn; -import integration.security.Sha256PasswordHasher; import javafx.fxml.FXML; -import javafx.scene.control.Button; -import persistence.UserDao; import ui.Page; - -import java.io.IOException; import java.util.function.Consumer; +/** + * Controller for the home page. + */ public class HomeController implements NavigationAware { private Consumer onNavigate; + /** + * Sets the navigation callback. + * @param onNavigate the callback for navigating to a page + */ public void setOnNavigate(Consumer onNavigate) { this.onNavigate = onNavigate; } diff --git a/src/main/java/ui/controller/MainController.java b/src/main/java/ui/controller/MainController.java index aaa182b..93d83be 100644 --- a/src/main/java/ui/controller/MainController.java +++ b/src/main/java/ui/controller/MainController.java @@ -11,14 +11,17 @@ import javafx.scene.Parent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.VBox; -import persistence.UserDao; +import persistence.dao.UserDao; import ui.DonationSession; import ui.Page; import ui.DonationSessionAware; import ui.controller.donation.DonationFlowController; - import java.io.IOException; +/** + * Main controller for the application. + * Manages page navigation, the donation flow, and the organization detail popup. + */ public class MainController { @FXML @@ -55,6 +58,12 @@ public void initialize() { } } + /** + * Loads and displays the given page in the main content area. + * Sets up the appropriate controller callbacks for each page. + * @param page the page to load + * @throws IOException if the FXML file cannot be loaded + */ public void loadPage(Page page) throws IOException { FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/" + page.getFileName())); Parent root = loader.load(); @@ -69,13 +78,6 @@ public void loadPage(Page page) throws IOException { } }); } - if (controller instanceof OrganizationController c) { - c.setOnDonate(org -> { - try { loadDonationPage(org); } - catch (IOException e) { throw new RuntimeException(e); } - }); - - } if (controller instanceof SignInController c) { c.setUserSignIn(new UserSignIn(new Sha256PasswordHasher(), new UserDao())); } @@ -99,10 +101,20 @@ public void loadPage(Page page) throws IOException { navbarController.showNavbar(); } + /** + * Starts the donation flow for the given organization. + * @param org the organization to donate to + * @throws IOException if the FXML file cannot be loaded + */ public void loadDonationPage(Organization org) throws IOException { donationFlowController.start(org); } + /** + * Shows the organization detail popup for the given organization. + * Fetches description and logo from Innsamlingskontrollen and displays them. + * @param org the organization to show details for + */ public void showOrganizationDetail(Organization org) { try { FXMLLoader loader = new FXMLLoader( diff --git a/src/main/java/ui/controller/MyProfileController.java b/src/main/java/ui/controller/MyProfileController.java index 2af08f7..b8dd62c 100644 --- a/src/main/java/ui/controller/MyProfileController.java +++ b/src/main/java/ui/controller/MyProfileController.java @@ -11,12 +11,16 @@ import javafx.scene.control.Label; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; -import persistence.DonationDao; +import persistence.dao.DonationDao; import ui.Page; import util.SessionManager; import java.util.function.Consumer; +/** + * Controller for the user profile page. + * Displays user information, donation statistics and donation history. + */ public class MyProfileController implements NavigationAware { private Consumer onNavigate; @@ -31,6 +35,10 @@ public class MyProfileController implements NavigationAware { @FXML private TableColumn amountColumn; @FXML private TableColumn dateColumn; + /** + * Sets the navigation callback. + * @param onNavigate the callback for navigating to a page + */ public void setOnNavigate(Consumer onNavigate) { this.onNavigate = onNavigate; } diff --git a/src/main/java/ui/controller/NavbarController.java b/src/main/java/ui/controller/NavbarController.java index 18a56ea..0be90c5 100644 --- a/src/main/java/ui/controller/NavbarController.java +++ b/src/main/java/ui/controller/NavbarController.java @@ -8,6 +8,10 @@ import ui.Page; import util.SessionManager; +/** + * Controller for the navigation bar. + * Handles navigation between pages and updates the active button state. + */ public class NavbarController { private Consumer onNavigate; @@ -20,10 +24,18 @@ public class NavbarController { @FXML private Button signInBtn; + /** + * Sets the navigation callback. + * @param onNavigate the callback for navigating to a page + */ public void setOnNavigate(Consumer onNavigate) { this.onNavigate = onNavigate; } + /** + * Highlights the navbar button corresponding to the given page. + * @param page the currently active page + */ public void setActivePage(Page page) { homeBtn.getStyleClass().remove("nav-button-active"); orgBtn.getStyleClass().remove("nav-button-active"); @@ -41,10 +53,16 @@ public void setActivePage(Page page) { } } + /** + * Navigates to the home page. + */ public void goHome() { onNavigate.accept(Page.HOME); } + /** + * Navigates to the sign in page or profile page depending on authentication state. + */ public void goToSignIn() { if (SessionManager.isSignedIn()) { onNavigate.accept(Page.PROFILE); @@ -53,11 +71,17 @@ public void goToSignIn() { } } + /** + * Navigates to the organizations page. + */ public void goToOrganizations() { onNavigate.accept(Page.ORGANIZATIONS); } - + /** + * Updates the sign in button text based on the user's authentication state. + * Shows "My Profile" if signed in, "Sign In" otherwise. + */ public void updateAuthButton() { if (SessionManager.isSignedIn()) { signInBtn.setText("My Profile"); @@ -66,17 +90,17 @@ public void updateAuthButton() { } } - public void clearActivePage() { - homeBtn.getStyleClass().remove("nav-button-active"); - orgBtn.getStyleClass().remove("nav-button-active"); - signInBtn.getStyleClass().remove("nav-button-active"); - } - + /** + * Hides the navbar. + */ public void hideNavbar() { navBar.setVisible(false); navBar.setManaged(false); } + /** + * Shows the navbar. + */ public void showNavbar() { navBar.setVisible(true); navBar.setManaged(true); diff --git a/src/main/java/ui/controller/OrganizationController.java b/src/main/java/ui/controller/OrganizationController.java index ede0dd3..00ad56c 100644 --- a/src/main/java/ui/controller/OrganizationController.java +++ b/src/main/java/ui/controller/OrganizationController.java @@ -8,7 +8,7 @@ import javafx.scene.control.TextField; import javafx.scene.layout.FlowPane; import javafx.scene.layout.VBox; -import persistence.OrganizationDao; +import persistence.dao.OrganizationDao; import ui.Page; import java.sql.SQLException; @@ -17,6 +17,10 @@ import java.util.function.Consumer; import java.util.stream.Collectors; +/** + * Controller for the organizations page. + * Displays a paginated list of approved organizations with search functionality. + */ public class OrganizationController implements NavigationAware{ @FXML @@ -39,15 +43,26 @@ public class OrganizationController implements NavigationAware{ private Consumer onDonate; private Consumer onShowDetail; - + /** + * Sets the navigation callback. + * @param onNavigate the callback for navigating to a page + */ public void setOnNavigate(Consumer onNavigate) { this.onNavigate = onNavigate; } + /** + * Sets the callback to invoke when the user clicks Donate on an organization card. + * @param onDonate the callback with the selected organization + */ public void setOnDonate(Consumer onDonate) { this.onDonate = onDonate; } + /** + * Sets the callback to invoke when the user clicks on an organization card. + * @param onShowDetail the callback with the selected organization + */ public void setOnShowDetail(Consumer onShowDetail) { this.onShowDetail = onShowDetail; } @@ -58,7 +73,6 @@ public void initialize() { allOrgs = dao.getAll().stream() .filter(org -> "approved".equals(org.getStatus())) .collect(Collectors.toList()); - System.out.println("Fetched: " + allOrgs.size() + " organizations"); filtrertOrgs = new ArrayList<>(allOrgs); showPage(); @@ -143,5 +157,4 @@ private void nextPage() { page++; showPage(); } - } diff --git a/src/main/java/ui/controller/OrganizationDetailController.java b/src/main/java/ui/controller/OrganizationDetailController.java index 96a8b74..0775ea0 100644 --- a/src/main/java/ui/controller/OrganizationDetailController.java +++ b/src/main/java/ui/controller/OrganizationDetailController.java @@ -3,17 +3,18 @@ import domain.organization.Organization; import domain.organization.OrganizationDetails; import javafx.fxml.FXML; -import javafx.scene.control.Button; import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; import javafx.scene.image.Image; import javafx.scene.image.ImageView; -import ui.Page; - import java.awt.Desktop; import java.net.URI; import java.util.function.Consumer; +/** + * Controller for the organization detail popup. + * Displays organization name, verified status, description and logo. + */ public class OrganizationDetailController { @FXML private Label nameLabel; @@ -28,6 +29,12 @@ public class OrganizationDetailController { private Consumer onDonate; private Runnable onClose; + /** + * Sets the organization and its details, and updates all UI elements. + * Cuts the description at 300 characters if it is too long. + * @param org the organization to display + * @param details the scraped details including description and logo URL + */ public void setOrganization(Organization org, OrganizationDetails details) { this.organization = org; nameLabel.setText(org.getName()); @@ -68,10 +75,18 @@ public void setOrganization(Organization org, OrganizationDetails details) { } } + /** + * Sets the callback to invoke when the user clicks Donate. + * @param onDonate the callback with the selected organization + */ public void setOnDonate(Consumer onDonate) { this.onDonate = onDonate; } + /** + * Sets the callback to invoke when the user closes the popup. + * @param onClose the callback to run on close + */ public void setOnClose(Runnable onClose) { this.onClose = onClose; } diff --git a/src/main/java/ui/controller/RegisterController.java b/src/main/java/ui/controller/RegisterController.java index a43a47d..db662f8 100644 --- a/src/main/java/ui/controller/RegisterController.java +++ b/src/main/java/ui/controller/RegisterController.java @@ -9,22 +9,40 @@ import java.util.function.Consumer; +/** + * Controller for the register page. + * Handles user input validation and registration. + */ public class RegisterController implements NavigationAware { - @FXML private TextField usernameField; - @FXML private TextField emailField; - @FXML private TextField phoneNumberField; - @FXML private PasswordField passwordField; - @FXML private PasswordField confirmPasswordField; - @FXML private Label errorLabel; + @FXML + private TextField usernameField; + @FXML + private TextField emailField; + @FXML + private TextField phoneNumberField; + @FXML + private PasswordField passwordField; + @FXML + private PasswordField confirmPasswordField; + @FXML + private Label errorLabel; private UserRegister userRegister; private Consumer onNavigate; + /** + * Sets the user register use case. + * @param userRegister the use case for registering a new user + */ public void setUserRegister(UserRegister userRegister) { this.userRegister = userRegister; } + /** + * Sets the navigation callback. + * @param onNavigate the callback for navigating to a page + */ public void setOnNavigate(Consumer onNavigate) { this.onNavigate = onNavigate; } @@ -61,9 +79,4 @@ private void handleRegister() { private void handleSignIn() { onNavigate.accept(Page.SIGN_IN); } - - @FXML - private void handleGoHome() { - onNavigate.accept(Page.HOME); - } } diff --git a/src/main/java/ui/controller/SignInController.java b/src/main/java/ui/controller/SignInController.java index 6541460..6cde88c 100644 --- a/src/main/java/ui/controller/SignInController.java +++ b/src/main/java/ui/controller/SignInController.java @@ -1,26 +1,20 @@ package ui.controller; -import application.security.PasswordHasher; -import application.user.UserRegister; import application.user.UserSignIn; import domain.user.User; -import integration.security.Sha256PasswordHasher; -import javafx.event.ActionEvent; import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.scene.Node; -import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.PasswordField; import javafx.scene.control.TextField; -import javafx.stage.Stage; -import persistence.UserDao; import ui.Page; import util.SessionManager; - import java.io.IOException; import java.util.function.Consumer; +/** + * Controller for the sign-in page. + * Handles user authentication and navigation after sign in. + */ public class SignInController implements NavigationAware { @FXML private TextField loginField; @@ -30,17 +24,24 @@ public class SignInController implements NavigationAware { private UserSignIn userSignIn; private Consumer onNavigate; + /** + * Sets the sign in use case. + * @param userSignIn the use case for signing in a user + */ public void setUserSignIn(UserSignIn userSignIn) { this.userSignIn = userSignIn; } + /** + * Sets the navigation callback. + * @param onNavigate the callback for navigating to a page + */ public void setOnNavigate(Consumer onNavigate) { this.onNavigate = onNavigate; } @FXML private void handleSignIn() { - String login = loginField.getText().trim(); String password = passwordField.getText().trim(); @@ -49,7 +50,6 @@ private void handleSignIn() { return; } - // nullstill eventuell gammel feilmelding errorLabel.setText(""); try { diff --git a/src/main/java/ui/controller/donation/DonationAmountController.java b/src/main/java/ui/controller/donation/DonationAmountController.java index 064c07d..2b35c83 100644 --- a/src/main/java/ui/controller/donation/DonationAmountController.java +++ b/src/main/java/ui/controller/donation/DonationAmountController.java @@ -9,6 +9,10 @@ import java.util.function.Consumer; +/** + * Controller for the donation amount selection page. + * Handles amount input validation and navigation in the donation flow. + */ public class DonationAmountController implements DonationSessionAware { @FXML private Label orgLabel; @@ -19,20 +23,35 @@ public class DonationAmountController implements DonationSessionAware { private Consumer onContinue; private Runnable onBack; + /** + * Sets the current donation session and updates the organization label. + * @param donationSession the active donation session + */ @Override public void setDonationSession(DonationSession donationSession) { this.donationSession = donationSession; orgLabel.setText("Donating to: " + donationSession.getOrganization().getName()); } + /** + * Sets the callback to invoke when the user continues to the next step. + * @param onContinue the callback with the updated donation session + */ public void setOnContinue(Consumer onContinue) { this.onContinue = onContinue; } + /** + * Sets the callback to invoke when the user navigates back. + * @param onBack the callback to run on back navigation + */ public void setOnBack(Runnable onBack) { this.onBack = onBack; } + /** + * Validates the entered amount and continues to the next step in the donation flow. + */ @FXML public void handleContinue() { if (amountField.getText().isBlank()) { diff --git a/src/main/java/ui/controller/donation/DonationConfirmationController.java b/src/main/java/ui/controller/donation/DonationConfirmationController.java index 7fe397c..5e2df03 100644 --- a/src/main/java/ui/controller/donation/DonationConfirmationController.java +++ b/src/main/java/ui/controller/donation/DonationConfirmationController.java @@ -8,7 +8,10 @@ import ui.controller.NavigationAware; import java.util.function.Consumer; - +/** + * Controller for the donation confirmation page. + * Displays the organization name and donation amount after a successful donation. + */ public class DonationConfirmationController implements NavigationAware, DonationSessionAware { @FXML private Label orgLabel; @@ -17,11 +20,19 @@ public class DonationConfirmationController implements NavigationAware, Donation private Consumer onNavigate; private DonationSession donationSession; + /** + * Sets the navigation callback. + * @param onNavigate the callback for navigating to a page + */ @Override public void setOnNavigate(Consumer onNavigate) { this.onNavigate = onNavigate; } + /** + * Sets the donation session and updates the confirmation labels. + * @param session the completed donation session + */ @Override public void setDonationSession(DonationSession session) { this.donationSession = session; diff --git a/src/main/java/ui/controller/donation/DonationFlowController.java b/src/main/java/ui/controller/donation/DonationFlowController.java index f9d014a..6b70027 100644 --- a/src/main/java/ui/controller/donation/DonationFlowController.java +++ b/src/main/java/ui/controller/donation/DonationFlowController.java @@ -14,6 +14,10 @@ import java.math.BigDecimal; import java.util.function.Consumer; +/** + * Manages the multi-step donation flow. + * Handles navigation between donation steps and hides the navbar during the flow. + */ public class DonationFlowController { private final BorderPane mainPane; private final NavbarController navbarController; @@ -21,11 +25,22 @@ public class DonationFlowController { private Consumer onNavigate; private final UserDonate userDonate = new UserDonate(); + /** + * Creates a new DonationFlowController. + * @param mainPane the main BorderPane to load donation steps into + * @param navbarController the navbar controller used to hide/show the navbar + */ public DonationFlowController(BorderPane mainPane, NavbarController navbarController) { this.mainPane = mainPane; this.navbarController = navbarController; } + /** + * Starts the donation flow for the given organization. + * Navigates to the amount step if signed in, otherwise to the not logged in step. + * @param org the organization to donate to + * @throws IOException if an FXML file cannot be loaded + */ public void start(Organization org) throws IOException { session = new DonationSession(); session.setOrganization(org); @@ -38,6 +53,7 @@ public void start(Organization org) throws IOException { } } + // Loads the "not logged in" page private void loadStep1() throws IOException { FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/DonationNotLoggedIn.fxml")); Parent root = loader.load(); @@ -48,6 +64,8 @@ private void loadStep1() throws IOException { controller.setOnNavigate(onNavigate); mainPane.setCenter(root); } + + // Loads the amount selection page private void loadStep2() throws IOException { FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/DonationAmount.fxml")); Parent root = loader.load(); @@ -72,6 +90,7 @@ private void loadStep2() throws IOException { mainPane.setCenter(root); } + // Loads the payment information page private void loadStep3() throws IOException { FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/DonationPayment.fxml")); Parent root = loader.load(); @@ -101,6 +120,8 @@ private void loadStep3() throws IOException { mainPane.setCenter(root); } + + // Loads the confirmation page private void loadStep4() throws IOException { FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/DonationConfirmation.fxml")); Parent root = loader.load(); @@ -113,6 +134,10 @@ private void loadStep4() throws IOException { mainPane.setCenter(root); } + /** + * Sets the callback for navigating to a page after the donation flow ends. + * @param onNavigate the navigation callback + */ public void setOnNavigate(Consumer onNavigate) { this.onNavigate = onNavigate; } diff --git a/src/main/java/ui/controller/donation/DonationNotLoggedInController.java b/src/main/java/ui/controller/donation/DonationNotLoggedInController.java index fa21d72..fcfe421 100644 --- a/src/main/java/ui/controller/donation/DonationNotLoggedInController.java +++ b/src/main/java/ui/controller/donation/DonationNotLoggedInController.java @@ -1,6 +1,6 @@ package ui.controller.donation; -import domain.organization.Organization; + import javafx.fxml.FXML; import javafx.scene.control.Label; import ui.DonationSession; @@ -10,6 +10,10 @@ import java.util.function.Consumer; +/** + * Controller for the "not logged in" page in the donation flow. + * Shown when a user tries to donate without being signed in. + */ public class DonationNotLoggedInController implements NavigationAware, DonationSessionAware { @FXML private Label orgLabel; @@ -17,12 +21,19 @@ public class DonationNotLoggedInController implements NavigationAware, DonationS private Consumer onNavigate; private DonationSession donationSession; - + /** + * Sets the navigation callback. + * @param onNavigate the callback for navigating to a page + */ @Override public void setOnNavigate(Consumer onNavigate) { this.onNavigate = onNavigate; } + /** + * Sets the donation session and updates the organization label. + * @param session the active donation session + */ @Override public void setDonationSession(DonationSession session) { this.donationSession = session; @@ -40,6 +51,5 @@ public void goToRegister() { public void handleBack() { onNavigate.accept(Page.ORGANIZATIONS); } - } diff --git a/src/main/java/ui/controller/donation/DonationPaymentController.java b/src/main/java/ui/controller/donation/DonationPaymentController.java index ece65d7..5c44b7c 100644 --- a/src/main/java/ui/controller/donation/DonationPaymentController.java +++ b/src/main/java/ui/controller/donation/DonationPaymentController.java @@ -5,9 +5,12 @@ import javafx.scene.control.TextField; import ui.DonationSession; import ui.DonationSessionAware; - import java.util.function.Consumer; +/** + * Controller for the payment information page in the donation flow. + * Handles validation of card number, expiry date and CVC. + */ public class DonationPaymentController implements DonationSessionAware { @FXML private Label orgLabel; @@ -21,6 +24,10 @@ public class DonationPaymentController implements DonationSessionAware { private Consumer onContinue; private Runnable onBack; + /** + * Sets the donation session and updates the organization and amount labels. + * @param donationSession the active donation session + */ @Override public void setDonationSession(DonationSession donationSession) { this.donationSession = donationSession; @@ -28,10 +35,18 @@ public void setDonationSession(DonationSession donationSession) { amountLabel.setText("Amount: " + donationSession.getAmount() + " kr"); } + /** + * Sets the callback to invoke when the user confirms the donation. + * @param onContinue the callback with the updated donation session + */ public void setOnContinue(Consumer onContinue) { this.onContinue = onContinue; } + + /** + * Validates payment information and continues to the confirmation step. + */ @FXML public void handleConfirmDonation() { String cardNumber = cardNumberField.getText().trim().replace(" ", ""); @@ -53,6 +68,10 @@ public void handleConfirmDonation() { onContinue.accept(donationSession); } + /** + * Sets the callback to invoke when the user navigates back. + * @param onBack the callback to run on back navigation + */ public void setOnBack(Runnable onBack) { this.onBack = onBack; } diff --git a/src/main/java/util/SessionManager.java b/src/main/java/util/SessionManager.java index e2a6d4f..3e40ea2 100644 --- a/src/main/java/util/SessionManager.java +++ b/src/main/java/util/SessionManager.java @@ -14,7 +14,7 @@ public class SessionManager { /** * Signs in a user. * Stores the user as the current session holder. - * @param the user tom sign in. + * @param user the user to sign in */ public static void signIn(User user) { SignedIn = true; From c04752c38fafbb8b52e653a2844a0eba985221f4 Mon Sep 17 00:00:00 2001 From: Marius Klepp Date: Thu, 16 Apr 2026 14:31:42 +0200 Subject: [PATCH 3/3] Documentation: Added javadoc and removed unused method and files --- src/main/java/application/user/UserLogin.java | 36 ++++ .../domain/organization/Organization.java | 1 - .../java/persistence/dao/DonationDao.java | 1 + .../java/application/user/UserLoginTest.java | 162 ++++++++++++++ .../application/user/UserRegisterTest.java | 170 +++++++++++++++ .../application/user/UserStatisticsTest.java | 132 ++++++++++++ src/test/java/domain/DonationTest.java | 115 ++++++++++ src/test/java/domain/OrganizationTest.java | 202 ++++++++++++++++++ src/test/java/domain/UserTest.java | 132 ++++++++++++ .../security/Sha256PasswordHasherTest.java | 26 +++ 10 files changed, 976 insertions(+), 1 deletion(-) create mode 100644 src/main/java/application/user/UserLogin.java create mode 100644 src/test/java/application/user/UserLoginTest.java create mode 100644 src/test/java/application/user/UserRegisterTest.java create mode 100644 src/test/java/application/user/UserStatisticsTest.java create mode 100644 src/test/java/integration/security/Sha256PasswordHasherTest.java diff --git a/src/main/java/application/user/UserLogin.java b/src/main/java/application/user/UserLogin.java new file mode 100644 index 0000000..50940ac --- /dev/null +++ b/src/main/java/application/user/UserLogin.java @@ -0,0 +1,36 @@ +package application.user; + +import application.security.PasswordHasher; +import domain.user.User; +import persistence.UserRepository; + +public class UserLogin { + + private final PasswordHasher hasher; + private final UserRepository repo; + + public UserLogin(PasswordHasher hasher, UserRepository repo) { + this.hasher = hasher; + this.repo = repo; + } + + public User execute(String login, String password) { + // normalize (trim etc.) + login = login.trim().toLowerCase(); + + User user = repo.findByLogin(login) + .orElseThrow(() -> new IllegalArgumentException("Invalid credentials")); + + // hash password (optional) + String hashedInput = hasher.hash(password); + + // compare hashed input with stored hash + if (!hashedInput.equals(user.getPassword())) { + throw new IllegalArgumentException("Invalid password"); + } + + System.out.println("Loggin in"); + // return user (if match) + return user; + } +} diff --git a/src/main/java/domain/organization/Organization.java b/src/main/java/domain/organization/Organization.java index 2c5ff30..fd7cec1 100644 --- a/src/main/java/domain/organization/Organization.java +++ b/src/main/java/domain/organization/Organization.java @@ -26,7 +26,6 @@ public class Organization { @JsonProperty("is_pre_approved") public boolean isPreApproved; - public Organization() {} /** diff --git a/src/main/java/persistence/dao/DonationDao.java b/src/main/java/persistence/dao/DonationDao.java index e8294ea..c4893ec 100644 --- a/src/main/java/persistence/dao/DonationDao.java +++ b/src/main/java/persistence/dao/DonationDao.java @@ -70,6 +70,7 @@ private Donation mapRow(ResultSet rs) throws SQLException { rs.getString("password_hash"), rs.getString("e_mail") ); + user.setId(rs.getLong("user_id")); Organization org = new Organization(); org.setOrgNumber(rs.getString("org_number")); diff --git a/src/test/java/application/user/UserLoginTest.java b/src/test/java/application/user/UserLoginTest.java new file mode 100644 index 0000000..4bd8312 --- /dev/null +++ b/src/test/java/application/user/UserLoginTest.java @@ -0,0 +1,162 @@ +package application.user; + +import application.security.PasswordHasher; +import domain.user.User; +import org.junit.jupiter.api.Test; +import persistence.UserRepository; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +public class UserLoginTest { + + @Test + void executeReturnsUserWhenCredentialsAreValid() { + User user = new User("testuser", "12345678", "hashed-secret", "test@example.com"); + PasswordHasher hasher = new FakePasswordHasher("hashed-secret"); + UserRepository repo = new FakeUserRepository(Optional.of(user)); + + UserLogin userLogin = new UserLogin(hasher, repo); + + User result = userLogin.execute("testuser", "secret"); + + assertSame(user, result); + + } + + @Test + void executeThrowsWhenLoginDoesNotExist() { + PasswordHasher hasher = new FakePasswordHasher("hashed-secret"); + UserRepository repo = new FakeUserRepository(Optional.empty()); + + UserLogin userLogin = new UserLogin(hasher, repo); + + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> userLogin.execute("missing-user", "secret") + ); + + assertEquals("Invalid credentials", exception.getMessage()); + } + + @Test + void executeThrowsWhenPasswordIsWrong() { + User user = new User("testuser", "12345678", "stored-hash", "test@example.com"); + PasswordHasher hasher = new FakePasswordHasher("different-hash"); + UserRepository repo = new FakeUserRepository(Optional.of(user)); + + UserLogin userLogin = new UserLogin(hasher, repo); + + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> userLogin.execute("testuser", "wrong-password") + ); + + assertEquals("Invalid password", exception.getMessage()); + } + + @Test + void executeUsesNormalizedLoginBeforeLookup() { + User user = new User("testuser", "12345678", "hashed-secret", "test@example.com"); + RecordingUserRepository repo = new RecordingUserRepository(Optional.of(user)); + PasswordHasher hasher = new FakePasswordHasher("hashed-secret"); + + UserLogin userLogin = new UserLogin(hasher, repo); + + userLogin.execute(" TestUser ", "secret"); + + assertEquals("testuser", repo.lastLoginUsed); + } + + private static class FakePasswordHasher implements PasswordHasher{ + private final String hashToReturn; + private FakePasswordHasher(String hashToReturn) { + this.hashToReturn = hashToReturn; + } + + @Override + public String hash(String password) { + return hashToReturn; + } + } + + private static class FakeUserRepository implements UserRepository { + private final Optional userToReturn; + + private FakeUserRepository(Optional userToReturn) { + this.userToReturn = userToReturn; + } + + @Override + public Optional findByUsername(String username){ + return Optional.empty(); + } + + @Override + public Optional findByEmail(String email){ + return Optional.empty(); + } + + @Override + public Optional findByLogin(String login) { + return userToReturn; + } + + @Override + public boolean existsByEmail(String email) { + return false; + } + + @Override + public boolean existsByUsername(String username) { + return false; + } + + @Override + public void insert(User user) { + } + } + + private static class RecordingUserRepository implements UserRepository { + private final Optional userToReturn; + private String lastLoginUsed; + + private RecordingUserRepository(Optional userToReturn) { + this.userToReturn = userToReturn; + } + + @Override + public Optional findByUsername(String username) { + return Optional.empty(); + } + + @Override + public Optional findByEmail(String email) { + return Optional.empty(); + } + + @Override + public Optional findByLogin(String login) { + lastLoginUsed = login; + return userToReturn; + } + + @Override + public boolean existsByEmail(String email) { + return false; + } + + @Override + public boolean existsByUsername(String username) { + return false; + } + + @Override + public void insert(User user) { + } + } + + +} + diff --git a/src/test/java/application/user/UserRegisterTest.java b/src/test/java/application/user/UserRegisterTest.java new file mode 100644 index 0000000..c5296cd --- /dev/null +++ b/src/test/java/application/user/UserRegisterTest.java @@ -0,0 +1,170 @@ +package application.user; + +import application.security.PasswordHasher; +import domain.user.User; +import org.junit.jupiter.api.Test; +import persistence.UserRepository; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +public class UserRegisterTest { + + @Test + void executeInsertsUserWhenInputIsValid() { + RecordingUserRepository repo = new RecordingUserRepository(); + PasswordHasher hasher = new FakePasswordHasher("hashed-password"); + UserRegister userRegister = new UserRegister(hasher, repo); + + userRegister.execute("testuser", "12345678", "secret", "test@example.com"); + + assertNotNull(repo.insertedUser); + assertEquals("testuser", repo.insertedUser.getUsername()); + assertEquals("12345678", repo.insertedUser.getPhoneNumber()); + assertEquals("hashed-password", repo.insertedUser.getPassword()); + assertEquals("test@example.com", repo.insertedUser.getEMail()); + } + + + @Test + void executeNormalizesUsernameEmailAndPhoneBeforeInsert() { + RecordingUserRepository repo = new RecordingUserRepository(); + PasswordHasher hasher = new FakePasswordHasher("hashed-password"); + UserRegister userRegister = new UserRegister(hasher, repo); + + userRegister.execute(" testuser ", " 12345678 ", "secret", " TEST@EXAMPLE.COM "); + + assertNotNull(repo.insertedUser); + assertEquals("testuser", repo.insertedUser.getUsername()); + assertEquals("12345678", repo.insertedUser.getPhoneNumber()); + assertEquals("test@example.com", repo.insertedUser.getEMail()); + } + + + @Test + void executeThrowsWhenEmailAlreadyExists() { + RecordingUserRepository repo = new RecordingUserRepository(); + repo.emailExists = true; + PasswordHasher hasher = new FakePasswordHasher("hashed-password"); + UserRegister userRegister = new UserRegister(hasher, repo); + + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> userRegister.execute("testuser", "12345678", "secret", "test@example.com") + ); + + assertEquals("Email already in use", exception.getMessage()); + assertNull(repo.insertedUser); + } + + @Test + void executeThrowsWhenUsernameAlreadyExists() { + RecordingUserRepository repo = new RecordingUserRepository(); + repo.usernameExists = true; + PasswordHasher hasher = new FakePasswordHasher("hashed-password"); + UserRegister userRegister = new UserRegister(hasher, repo); + + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> userRegister.execute("testuser", "12345678", "secret", "test@example.com") + ); + + assertEquals("This username is taken", exception.getMessage()); + assertNull(repo.insertedUser); + } + + @Test + void executeHashesPasswordBeforeSavingUser() { + RecordingUserRepository repo = new RecordingUserRepository(); + RecordingPasswordHasher hasher = new RecordingPasswordHasher("hashed-password"); + UserRegister userRegister = new UserRegister(hasher, repo); + + userRegister.execute("testuser", "12345678", "secret", "test@example.com"); + + assertEquals("secret", hasher.lastPasswordInput); + assertNotNull(repo.insertedUser); + assertEquals("hashed-password", repo.insertedUser.getPassword()); + } + + @Test + void executePropagatesDomainValidationErrors() { + RecordingUserRepository repo = new RecordingUserRepository(); + PasswordHasher hasher = new FakePasswordHasher("hashed-password"); + UserRegister userRegister = new UserRegister(hasher, repo); + + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> userRegister.execute("testuser", "1234", "secret", "test@example.com") + ); + + assertEquals("Fill in a phonenumber with 8 digits", exception.getMessage()); + assertNull(repo.insertedUser); + } + + private static class FakePasswordHasher implements PasswordHasher { + private final String hashToReturn; + + private FakePasswordHasher(String hashToReturn) { + this.hashToReturn = hashToReturn; + } + + @Override + public String hash(String password) { + return hashToReturn; + } + } + + + private static class RecordingPasswordHasher implements PasswordHasher { + private final String hashToReturn; + private String lastPasswordInput; + + private RecordingPasswordHasher(String hashToReturn) { + this.hashToReturn = hashToReturn; + } + + @Override + public String hash(String password) { + lastPasswordInput = password; + return hashToReturn; + } + } + + private static class RecordingUserRepository implements UserRepository { + private boolean emailExists; + private boolean usernameExists; + private User insertedUser; + + @Override + public Optional findByUsername(String username) { + return Optional.empty(); + } + + @Override + public Optional findByEmail(String email) { + return Optional.empty(); + } + + @Override + public Optional findByLogin(String login) { + return Optional.empty(); + } + + + @Override + public boolean existsByEmail(String email) { + return emailExists; + } + + @Override + public boolean existsByUsername(String username) { + return usernameExists; + } + + @Override + public void insert(User user) { + insertedUser = user; + } + } +} diff --git a/src/test/java/application/user/UserStatisticsTest.java b/src/test/java/application/user/UserStatisticsTest.java new file mode 100644 index 0000000..da904cb --- /dev/null +++ b/src/test/java/application/user/UserStatisticsTest.java @@ -0,0 +1,132 @@ +package application.user; + +import domain.user.User; +import org.junit.jupiter.api.Test; +import persistence.DonationDao; + +import java.lang.reflect.Field; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class UserStatisticsTest { + + @Test + void userFavoriteOrganizationsReturnsListFromDonationDao() throws Exception { + FakeDonationDao fakeDao = new FakeDonationDao(); + fakeDao.favoriteOrganizationsToReturn = List.of( + "Org1 (3 donasjoner)", + "Org2 (2 donasjoner)" + ); + + UserStatistics statistics = createStatisticsWithFakeDao(fakeDao); + User user = createUserWithId(42L); + + List result = statistics.userFavoriteOrganizations(user); + + assertEquals(List.of("Org1 (3 donasjoner)", "Org2 (2 donasjoner)"), result); + assertEquals(42L, fakeDao.lastUserIdForFavorites); + } + + @Test + void userDonationsReturnsListFromDonationDao() throws Exception { + FakeDonationDao fakeDao = new FakeDonationDao(); + fakeDao.userDonationsToReturn = List.of( + "Amount: 100, Date: 2026-04-15, Organization: Org1", + "Amount: 50, Date: 2026-04-16, Organization: Org2" + ); + + UserStatistics statistics = createStatisticsWithFakeDao(fakeDao); + User user = createUserWithId(7L); + + List result = statistics.userDonations(user); + + assertEquals(List.of( + "Amount: 100, Date: 2026-04-15, Organization: Org1", + "Amount: 50, Date: 2026-04-16, Organization: Org2" + ), result); + assertEquals(7L, fakeDao.lastUserIdForDonations); + } + + @Test + void userTotalDonationAmountReturnsValueFromDonationDao() throws Exception { + FakeDonationDao fakeDao = new FakeDonationDao(); + fakeDao.totalDonationAmountToReturn = "250.00"; + + UserStatistics statistics = createStatisticsWithFakeDao(fakeDao); + User user = createUserWithId(15L); + + String result = statistics.userTotalDonationAmount(user); + + assertEquals("250.00", result); + assertEquals(15L, fakeDao.lastUserIdForTotalAmount); + } + + @Test + void getTotalDonationsMadeReturnsValueFromDonationDao() throws Exception { + FakeDonationDao fakeDao = new FakeDonationDao(); + fakeDao.totalDonationsMadeToReturn = "5"; + + UserStatistics statistics = createStatisticsWithFakeDao(fakeDao); + User user = createUserWithId(99L); + + String result = statistics.getTotalDonationsMade(user); + + assertEquals("5", result); + assertEquals(99L, fakeDao.lastUserIdForTotalCount); + } + + + + private UserStatistics createStatisticsWithFakeDao(FakeDonationDao fakeDao) throws Exception { + UserStatistics statistics = new UserStatistics(); + + Field field = UserStatistics.class.getDeclaredField("donationDao"); + field.setAccessible(true); + field.set(statistics, fakeDao); + + return statistics; + } + + private User createUserWithId(long id) { + User user = new User("testuser", "12345678", "password", "test@example.com"); + user.setId(id); + return user; + } + + private static class FakeDonationDao extends DonationDao { + private List favoriteOrganizationsToReturn = List.of(); + private List userDonationsToReturn = List.of(); + private String totalDonationAmountToReturn = "0"; + private String totalDonationsMadeToReturn = "0"; + + private long lastUserIdForFavorites; + private long lastUserIdForDonations; + private long lastUserIdForTotalAmount; + private long lastUserIdForTotalCount; + + @Override + public List getFavoriteOrganizations(long userId) { + lastUserIdForFavorites = userId; + return favoriteOrganizationsToReturn; + } + + @Override + public List getUserDonations(long userId) { + lastUserIdForDonations = userId; + return userDonationsToReturn; + } + + @Override + public String getTotalDonationAmount(long userId) { + lastUserIdForTotalAmount = userId; + return totalDonationAmountToReturn; + } + + @Override + public String getTotalDonationsMade(long userId) { + lastUserIdForTotalCount = userId; + return totalDonationsMadeToReturn; + } + } +} diff --git a/src/test/java/domain/DonationTest.java b/src/test/java/domain/DonationTest.java index bb9c875..ea73049 100644 --- a/src/test/java/domain/DonationTest.java +++ b/src/test/java/domain/DonationTest.java @@ -1,5 +1,120 @@ package domain; +import domain.donation.Cause; +import domain.donation.Donation; +import domain.organization.Organization; +import domain.user.User; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.time.Duration; +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + class DonationTest { + //User Variables + private final String testUserName = "testUser"; + private final String testPhoneNumber = "90909090"; + private final String testEmail = "test@email.com"; + private final String testPassword = "password"; + + //Organization Variables + private final String testName = "Name"; + private final String testOrgNr = "123456789"; + private final Cause testCause = Cause.HEALTH; + private final String testDescription = "Description"; + private final String testContactEmail = "test@mail.com"; + private final String testWebsite = "https://test.com"; + private final boolean testVerified = true; + + //Donation Variables + private Long testID = 123L; + private BigDecimal testAmount = new BigDecimal("1500"); + private LocalDateTime testDateTime = LocalDateTime.of(2026, 3, 5, 8, 30, 0); + private User testUser; + private Organization testOrganization; + + @BeforeEach + public void init() { + testUser = new User(testUserName, testPhoneNumber, testPassword, testEmail); + testOrganization = new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified); + } + + + @Test + public void DonationTestPositive(){ + Donation donation = new Donation(testAmount, testUser, testOrganization); + } + + @Test + public void DonationTestUserNull(){ + testUser = null; + NullPointerException Exception = assertThrows(NullPointerException.class, + () -> new Donation (testAmount, testUser, testOrganization)); + + assertEquals("User cannot be null", Exception.getMessage()); + } + + @Test + public void DonationOrganizationNull(){ + testOrganization = null; + NullPointerException Exception = assertThrows(NullPointerException.class, + () -> new Donation (testAmount, testUser, testOrganization)); + + assertEquals("Organization cannot be null", Exception.getMessage()); + } + + @Test + public void DonationTestAmountNegative(){ + testAmount = new BigDecimal("-23"); + IllegalArgumentException Exception = assertThrows(IllegalArgumentException.class, + () -> new Donation (testAmount, testUser, testOrganization)); + + assertEquals("Amount must be greater than 0", Exception.getMessage()); + } + + //Getter tests + /* id is never set, getter makes a NullPointerException + @Test + public void DonationTestIdGet(){ + Donation donation = new Donation(testAmount, testUser, testOrganization); + assertEquals(0L, donation.getId()); + + } + */ + + @Test + public void DonationTestAmountGet(){ + Donation donation = new Donation(testAmount, testUser, testOrganization); + assertEquals(testAmount, donation.getAmount()); + + } + + @Test + public void DonationTestDateTimeGet(){ + LocalDateTime now = LocalDateTime.now(); + Donation donation = new Donation(testAmount, testUser, testOrganization); + + assertTrue(Duration.between(now, donation.getDateTime()).abs().toMillis() < 1000); + + } + + @Test + public void DonationTestUserGet(){ + Donation donation = new Donation(testAmount, testUser, testOrganization); + assertEquals(testUser, donation.getUser()); + + } + + @Test + public void DonationTestOrganizationGet(){ + Donation donation = new Donation(testAmount, testUser, testOrganization); + assertEquals(testOrganization, donation.getOrganization()); + + } } \ No newline at end of file diff --git a/src/test/java/domain/OrganizationTest.java b/src/test/java/domain/OrganizationTest.java index 815aa53..47c8024 100644 --- a/src/test/java/domain/OrganizationTest.java +++ b/src/test/java/domain/OrganizationTest.java @@ -1,5 +1,207 @@ package domain; +import domain.donation.Cause; +import domain.organization.Organization; +import domain.user.User; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + class OrganizationTest { + private String testName = "Name"; + private String testOrgNr = "123456789"; + private Cause testCause = Cause.HEALTH; + private String testDescription = "Description"; + private String testContactEmail = "test@mail.com"; + private String testWebsite = "https://test.com"; + private boolean testVerified = true; + + //Positive tests + @Test + public void organizationTestPositive(){ + Organization organization = new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified); + } + + //Negative tests + @Test + public void organizationTestNameBlank(){ + testName = ""; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new Organization("", testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified)); + + assertEquals("Name cannot be null or blank", exception.getMessage()); + } + + @Test + public void organizationTestNameNull(){ + testName = null; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified)); + + assertEquals("Name cannot be null or blank", exception.getMessage()); + } + + @Test + public void organizationTestOrgNrNull(){ + testOrgNr = null; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified)); + + assertEquals("Organization number must be 9 digits", exception.getMessage()); + } + + @Test + public void organizationTestOrgNrLetters(){ + testOrgNr = "12345678a"; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified)); + + assertEquals("Organization number must be 9 digits", exception.getMessage()); + } + + @Test + public void organizationTestOrgNrShort(){ + testOrgNr = "12345"; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified)); + + assertEquals("Organization number must be 9 digits", exception.getMessage()); + } + + /* Mangler exception for når email er null + @Test + public void organizationTestContactEmailNull(){ + testContactEmail = null; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified)); + } + */ + + @Test + public void organizationTestContactEmailTrim(){ + testContactEmail = "test@mail.com "; + Organization organization = new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified); + } + + @Test + public void organizationTestContactEmailBlank(){ + testContactEmail = ""; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified)); + + assertEquals("Email cannot be blank", exception.getMessage()); + } + + @Test + public void organizationTestContactEmailMissingAt(){ + testContactEmail = "test.com"; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified)); + + assertEquals("Invalid email format", exception.getMessage()); + } + + @Test + public void organizationTestContactEmailDoubleAt(){ + testContactEmail = "test@@mail.com"; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified)); + + assertEquals("Invalid email format", exception.getMessage()); + } + + @Test + public void organizationTestContactEmailMisplacedAt(){ + testContactEmail = "@testmail.com"; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified)); + + assertEquals("Invalid email format", exception.getMessage()); + } + + @Test + public void organizationTestContactEmailMissingDot(){ + testContactEmail = "test@mailcom"; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified)); + + assertEquals("Invalid email format", exception.getMessage()); + } + + @Test + public void organizationTestWebsiteMissingHttps(){ + testWebsite = "test.com"; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified)); + + assertEquals("Website must start with http:// or https://", exception.getMessage()); + } + + /* Mangler exception når url mangler domain extension (.com f.eks) + @Test + public void organizationTestWebsiteMissingDot(){ + testWebsite = "https://testcom"; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified)); + + assertEquals("Website must start with http:// or https://", exception.getMessage()); + } + */ + + /* Mangler exception når website er null + @Test + public void organizationTestWebsiteMissingDot(){ + testWebsite = null; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified)); + + assertEquals("Website must start with http:// or https://", exception.getMessage()); + } + */ + + + + //Getter tests + @Test + public void organizationTestNameGet(){ + Organization Organization = new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified); + assertEquals(testName, Organization.getName()); + } + + @Test + public void organizationTestOrgNrGet(){ + Organization Organization = new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified); + assertEquals(testOrgNr, Organization.getOrgNr()); + } + + @Test + public void organizationTestCauseGet(){ + Organization Organization = new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified); + assertEquals(testCause, Organization.getCause()); + } + + @Test + public void organizationDescriptionGet(){ + Organization Organization = new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified); + assertEquals(testDescription, Organization.getDescription()); + } + + @Test + public void organizationTestContactEmailGet(){ + Organization Organization = new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified); + assertEquals(testContactEmail, Organization.getContactEmail()); + } + + @Test + public void organizationTestWebsiteGet(){ + Organization Organization = new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified); + assertEquals(testWebsite, Organization.getWebsite()); + } + @Test + public void organizationTestIsVerifiedGet(){ + Organization Organization = new Organization(testName, testOrgNr, testCause, testDescription, testContactEmail, testWebsite, testVerified); + assertEquals(testVerified, Organization.isVerified()); + } } \ No newline at end of file diff --git a/src/test/java/domain/UserTest.java b/src/test/java/domain/UserTest.java index c21aa90..b1c8f14 100644 --- a/src/test/java/domain/UserTest.java +++ b/src/test/java/domain/UserTest.java @@ -1,5 +1,137 @@ package domain; +import domain.user.User; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + class UserTest { + private String testUserName = "testUser"; + private String testPhoneNumber = "90909090"; + private String testEmail = "test@email.com"; + private final String testPassword = "password"; + + //Positive tests + @Test + public void userTestPositive(){ + User User = new User(testUserName, testPhoneNumber, testPassword, testEmail); + } + + //Negative tests + @Test + public void userTestUserNameBlank(){ + testUserName = ""; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new User(testUserName, testPhoneNumber, testPassword, testEmail)); + + assertEquals("Username has to be filled in", exception.getMessage()); + } + + @Test + public void userTestUserNameNull(){ + testUserName = null; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new User(testUserName, testPhoneNumber, testPassword, testEmail)); + + assertEquals("Username has to be filled in", exception.getMessage()); + } + + @Test + public void userTestPhoneNumberBlank(){ + testPhoneNumber = ""; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new User(testUserName, testPhoneNumber, testPassword, testEmail)); + + assertEquals("Phonenumber has to be filled in", exception.getMessage()); + } + + @Test + public void userTestPhoneNumberNull(){ + testPhoneNumber = null; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new User(testUserName, testPhoneNumber, testPassword, testEmail)); + + assertEquals("Phonenumber has to be filled in", exception.getMessage()); + } + + @Test + public void userTestPhoneNumberShort() { + testPhoneNumber = "909090"; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new User(testUserName, testPhoneNumber, testPassword, testEmail)); + + assertEquals("Fill in a phonenumber with 8 digits", exception.getMessage()); + } + + @Test + public void userTestEmailBlank(){ + testEmail = ""; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new User(testUserName, testPhoneNumber, testPassword, testEmail)); + + assertEquals("E-mail has to be filled in", exception.getMessage()); + } + + @Test + public void userTestEmailNull(){ + testEmail = null; + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> new User(testUserName, testPhoneNumber, testPassword, testEmail)); + + assertEquals("E-mail has to be filled in", exception.getMessage()); + } + + //Getter tests + @Test + public void userTestUserNameGet(){ + User User = new User(testUserName, testPhoneNumber, testPassword, testEmail); + assertEquals(User.getUsername(), testUserName); + } + + @Test + public void userTestPhoneNumberGet(){ + User User = new User(testUserName, testPhoneNumber, testPassword, testEmail); + assertEquals(User.getPhoneNumber(), testPhoneNumber); + } + + @Test + public void userTestEmailGet(){ + User User = new User(testUserName, testPhoneNumber, testPassword, testEmail); + assertEquals(User.getEMail(), testEmail); + } + + @Test + public void userTestPasswordGet(){ + User User = new User(testUserName, testPhoneNumber, testPassword, testEmail); + assertEquals(User.getPassword(), testPassword); + } + + //Setter tests + @Test + public void userTestPasswordSet(){ + User User = new User(testUserName, testPhoneNumber, testPassword, testEmail); + String newPassword = "NewPassoword"; + User.setPassword(newPassword); + assertEquals(newPassword, User.getPassword()); + } + + @Test + public void userTestUserNameSet(){ + User User = new User(testUserName, testPhoneNumber, testPassword, testEmail); + String newUserName = "NewUsername"; + User.setUsername(newUserName); + assertEquals(newUserName, User.getUsername()); + } + + @Test + public void userTestPhoneNumberSet(){ + User User = new User(testUserName, testPhoneNumber, testPassword, testEmail); + String newPhoneNumber = "123456asdsad7"; //Trenger input validering + User.setPhonenumber(newPhoneNumber); + assertEquals(newPhoneNumber, User.getPhoneNumber()); + } + + + } \ No newline at end of file diff --git a/src/test/java/integration/security/Sha256PasswordHasherTest.java b/src/test/java/integration/security/Sha256PasswordHasherTest.java new file mode 100644 index 0000000..67b360e --- /dev/null +++ b/src/test/java/integration/security/Sha256PasswordHasherTest.java @@ -0,0 +1,26 @@ +package integration.security; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class Sha256PasswordHasherTest { + @Test + void hashReturnsExpectedSha256ForKnownInput() { + Sha256PasswordHasher hasher = new Sha256PasswordHasher(); + + String result = hasher.hash("password"); + + assertEquals("5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8", result); + } + + @Test + void hashReturns64CharacterLowercaseHexString() { + Sha256PasswordHasher hasher = new Sha256PasswordHasher(); + + String result = hasher.hash("secret"); + + assertEquals(64, result.length()); + assertTrue(result.matches("[0-9a-f]{64}")); + } +}