From c4955dea787a2947777370af829e639f9e2a42c7 Mon Sep 17 00:00:00 2001 From: Lucy Ciara Herud-Thomassen <86323303+LucyCiara@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:51:22 +0200 Subject: [PATCH 1/7] update[DbWrapper]: add exception throwing for SQLException edgecase for use in testing --- src/main/java/edu/group5/app/model/wrapper/DbWrapper.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/group5/app/model/wrapper/DbWrapper.java b/src/main/java/edu/group5/app/model/wrapper/DbWrapper.java index f357ae1..fd2a2cc 100644 --- a/src/main/java/edu/group5/app/model/wrapper/DbWrapper.java +++ b/src/main/java/edu/group5/app/model/wrapper/DbWrapper.java @@ -230,8 +230,10 @@ private List importDonations(int user_id, boolean all) { * @throws IllegalArgumentException This exception is thrown when data is null, its rows are not * of length 6, any of the rows are null, any of the rows are duplicates or existing rows in * the database, or any of the values in the rows can't be cast to the correct data-types. + * @throws SQLException Is thrown when an unexpected exception like trying to export a number that's + * too big happens. */ - public int exportDonations(List data) throws IllegalArgumentException { + public int exportDonations(List data) throws IllegalArgumentException, SQLException { this.fetchAllDonations(); ParameterValidator.exportChecker(data, "data", this.donations, 6); @@ -263,6 +265,7 @@ public int exportDonations(List data) throws IllegalArgumentException this.logger.info("Donations exported"); } catch (SQLException e) { this.logger.log(Level.SEVERE, "Unexpected SQL exception", e); + throw new SQLException("An unexpected SQL exception has occurred. This might be caused by inserting an item that is too large."); } finally { this.close(null, ps); } From f0430cdec860f44e89ed2feca8273fc3115aa7ae Mon Sep 17 00:00:00 2001 From: Lucy Ciara Herud-Thomassen <86323303+LucyCiara@users.noreply.github.com> Date: Tue, 21 Apr 2026 18:23:18 +0200 Subject: [PATCH 2/7] test[DbWrapper]: add testing for SQLException --- .../model/wrapper/DbWrapperDonationsTest.java | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/test/java/edu/group5/app/model/wrapper/DbWrapperDonationsTest.java b/src/test/java/edu/group5/app/model/wrapper/DbWrapperDonationsTest.java index 1db7a90..f9cc8ff 100644 --- a/src/test/java/edu/group5/app/model/wrapper/DbWrapperDonationsTest.java +++ b/src/test/java/edu/group5/app/model/wrapper/DbWrapperDonationsTest.java @@ -7,6 +7,7 @@ import java.math.BigDecimal; import java.math.RoundingMode; +import java.sql.SQLException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; @@ -25,12 +26,14 @@ public class DbWrapperDonationsTest { private Object[] cutoffDonation; private Object[] freakyDonation; private Object[] repeatingDonation; + private Object[] tooBigDonation; private List donations; private List donations2; private List donations3; private List repeatedDonations; private List wrongFormatDonations; private List wrongDatatypeDonations; + private List tooBigDonations; private List nullList; private static final int PRECISION = 5; @@ -40,10 +43,10 @@ public class DbWrapperDonationsTest { @BeforeEach void init() { this.db = new DbWrapper(true); - String[] firstNames = new String[] { "John", "Jane", "Cutoff", "Freaky", "Repeating" }; - String[] lastNames = new String[] { "Doe", "Doe", "Joh", "Bill", "JoeJoe" }; + String[] firstNames = new String[] { "John", "Jane", "Cutoff", "Freaky", "Repeating", "Big" }; + String[] lastNames = new String[] { "Doe", "Doe", "Joh", "Bill", "JoeJoe", "Willy" }; this.users = new ArrayList(); - for (int i = 0; i < 5; i++) { + for (int i = 0; i < firstNames.length; i++) { Object[] row = new Object[6]; row[0] = i + 1; row[1] = "Customer"; @@ -84,6 +87,13 @@ void init() { this.wrongDatatypeDonations = new ArrayList(); this.wrongDatatypeDonations.add(freakyDonation); + this.tooBigDonation = new Object[] { + 6, 6, 999999, new BigDecimal("9999999999999999999999999999999"), + new Timestamp(new Date().getTime()), "Azerbaijani technologies" + }; + this.tooBigDonations = new ArrayList(); + this.tooBigDonations.add(tooBigDonation); + Object[] nullRow = new Object[] {null, null, null, null, null, null}; this.nullList = new ArrayList(); this.nullList.add(nullRow); @@ -150,7 +160,7 @@ public void wronglyDatatypedDonationsThrowsExpectedException() { @Test public void addingSameDonationTwiceThrowsExpectedException() { assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 0); - assertEquals(1, this.db.exportDonations(this.donations)); + assertDoesNotThrow(() -> assertEquals(1, this.db.exportDonations(this.donations))); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { this.db.exportDonations(this.donations); }); @@ -163,7 +173,7 @@ public void addingSameDonationTwiceThrowsExpectedException() { public void addingSameDonationTwiceThrowsExpectedException2() { assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 0); assertTrue(this.db.importDonations((int) this.users.get(1)[0]).size() == 0); - assertEquals(2, this.db.exportDonations(this.donations2)); + assertDoesNotThrow(() -> assertEquals(2, this.db.exportDonations(this.donations2))); IllegalArgumentException exception = assertThrows( IllegalArgumentException.class, () -> { @@ -212,8 +222,20 @@ public void addingDonationListWithNullInRowThrowsExpectedException() { @Test public void dataIsEmptyAfterExportingAndImportingEmptyList() { assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 0); - assertEquals(0, this.db.exportDonations(new ArrayList())); + assertDoesNotThrow(() -> assertEquals(0, this.db.exportDonations(new ArrayList()))); assertTrue(this.db.importDonations((int) this.users.get(0)[0]).size() == 0); assertTrue(this.db.disconnect()); } + + @Test + public void exportTooBigNumberThrowsSQLException() { + SQLException exception = assertThrows(SQLException.class, () -> { + this.db.exportDonations(this.tooBigDonations); + }); + assertEquals( + "An unexpected SQL exception has occurred. " + + "This might be caused by inserting an item that is too large.", + exception.getMessage() + ); + } } From d6016d5cca6ebfb7ab435b6c41ca1deb9eb9c3b4 Mon Sep 17 00:00:00 2001 From: Lucy Ciara Herud-Thomassen <86323303+LucyCiara@users.noreply.github.com> Date: Tue, 21 Apr 2026 18:41:55 +0200 Subject: [PATCH 3/7] update[DbWrapper]: add exception throwing for exportUsers in case of SQLException --- src/main/java/edu/group5/app/model/wrapper/DbWrapper.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/group5/app/model/wrapper/DbWrapper.java b/src/main/java/edu/group5/app/model/wrapper/DbWrapper.java index fd2a2cc..3325b8c 100644 --- a/src/main/java/edu/group5/app/model/wrapper/DbWrapper.java +++ b/src/main/java/edu/group5/app/model/wrapper/DbWrapper.java @@ -132,8 +132,10 @@ public List importUsers() { * @throws IllegalArgumentException This exception is thrown when data is null, its rows are not * of length 6, any of the rows are null, any of the rows are duplicates or existing rows in * the database, or any of the values in the rows can't be cast to the correct data-types. + * @throws SQLException Is thrown when an unexpected exception like trying to export a number + * that's too big happens. */ - public int exportUsers(List data) throws IllegalArgumentException { + public int exportUsers(List data) throws IllegalArgumentException, SQLException { this.importUsers(); ParameterValidator.exportChecker(data, "data", this.users, 6); @@ -163,6 +165,7 @@ public int exportUsers(List data) throws IllegalArgumentException { this.logger.info("Users exported"); } catch (SQLException e) { this.logger.log(Level.SEVERE, "Unexpected SQL exception", e); + throw new SQLException("An unexpected SQL exception has occurred. This might be caused by inserting an item that is too large."); } finally { this.close(null, ps); } From 35f4d49fe2aa902f180ab8c3f61e5bd49bb09f86 Mon Sep 17 00:00:00 2001 From: Lucy Ciara Herud-Thomassen <86323303+LucyCiara@users.noreply.github.com> Date: Tue, 21 Apr 2026 18:46:12 +0200 Subject: [PATCH 4/7] test&update[DbWrapper]: add test for exporting user with too long String and handle exception throwing in DonationsTest --- .../model/wrapper/DbWrapperDonationsTest.java | 6 ++++- .../app/model/wrapper/DbWrapperUserTest.java | 27 +++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/test/java/edu/group5/app/model/wrapper/DbWrapperDonationsTest.java b/src/test/java/edu/group5/app/model/wrapper/DbWrapperDonationsTest.java index f9cc8ff..289b803 100644 --- a/src/test/java/edu/group5/app/model/wrapper/DbWrapperDonationsTest.java +++ b/src/test/java/edu/group5/app/model/wrapper/DbWrapperDonationsTest.java @@ -99,7 +99,11 @@ void init() { this.nullList.add(nullRow); this.db.connect(); - this.db.exportUsers(users); + try { + this.db.exportUsers(users); + } catch (Exception e) { + // This exception won't happen + } } private static boolean donationEquals(Object[] array1, Object[] array2) { diff --git a/src/test/java/edu/group5/app/model/wrapper/DbWrapperUserTest.java b/src/test/java/edu/group5/app/model/wrapper/DbWrapperUserTest.java index 00107de..e8621d8 100644 --- a/src/test/java/edu/group5/app/model/wrapper/DbWrapperUserTest.java +++ b/src/test/java/edu/group5/app/model/wrapper/DbWrapperUserTest.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -20,12 +21,14 @@ public class DbWrapperUserTest { private Object[] cutoffJoh; private Object[] freakyBill; private Object[] repeatingJoeJoe; + private Object[] bigWilly; private List users; private List users2; private List users3; private List repeatedUsers; private List wrongFormatUsers; private List wrongDatatypeUsers; + private List tooBigUsers; private List nullList; private DbWrapper db; @@ -60,6 +63,12 @@ void init() { this.wrongDatatypeUsers = new ArrayList(); this.wrongDatatypeUsers.add(freakyBill); + this.bigWilly = new Object[] { + 6, "Customer", "Big", "Willy", "bigdwilly@waaaaaytoolargemail.com", "passssssssssssword" + }; + this.tooBigUsers = new ArrayList(); + this.tooBigUsers.add(this.bigWilly); + Object[] nullRow = new Object[] {null, null, null, null, null, null}; this.nullList = new ArrayList(); this.nullList.add(nullRow); @@ -122,7 +131,7 @@ public void wronglyDatatypedUsersThrowsExpectedException() { public void addingSameUserTwiceThrowsExpectedException() { assertTrue(this.db.connect()); assertTrue(this.db.importUsers().size() == 0); - assertEquals(1, this.db.exportUsers(this.users)); + assertDoesNotThrow(() -> assertEquals(1, this.db.exportUsers(this.users))); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { this.db.exportUsers(this.users); }); @@ -135,7 +144,7 @@ public void addingSameUserTwiceThrowsExpectedException() { public void addingSameUserTwiceThrowsExpectedException2() { assertTrue(this.db.connect()); assertTrue(this.db.importUsers().size() == 0); - assertEquals(2, this.db.exportUsers(this.users2)); + assertDoesNotThrow(() -> assertEquals(2, this.db.exportUsers(this.users2))); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { this.db.exportUsers(this.users); }); @@ -179,4 +188,18 @@ public void addingUserListWithNullInRowThrowsExpectedException() { assertTrue(this.db.disconnect()); assertEquals("One or more rows in data contains null values", exception.getMessage()); } + + @Test + public void exportTooLongNameThrowsSQLException() { + assertTrue(this.db.connect()); + assertTrue(this.db.importUsers().size() == 0); + SQLException exception = assertThrows(SQLException.class, () -> { + this.db.exportUsers(this.tooBigUsers); + }); + assertEquals( + "An unexpected SQL exception has occurred. " + + "This might be caused by inserting an item that is too large.", + exception.getMessage() + ); + } } From 9fbe278b908494822d40053a31474b99414adc8b Mon Sep 17 00:00:00 2001 From: Lucy Ciara Herud-Thomassen <86323303+LucyCiara@users.noreply.github.com> Date: Tue, 21 Apr 2026 21:38:03 +0200 Subject: [PATCH 5/7] feat[AuthController]: add restrictions for user input that's too long Add checks for user input that's too long and show corresponding error messages in GUI --- .../group5/app/control/AuthController.java | 106 +++++++++++++++--- 1 file changed, 88 insertions(+), 18 deletions(-) diff --git a/src/main/java/edu/group5/app/control/AuthController.java b/src/main/java/edu/group5/app/control/AuthController.java index 11c3288..347a52e 100644 --- a/src/main/java/edu/group5/app/control/AuthController.java +++ b/src/main/java/edu/group5/app/control/AuthController.java @@ -9,6 +9,10 @@ import javafx.scene.control.Alert; import javafx.scene.control.ButtonType; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; /** @@ -74,44 +78,110 @@ public User getCurrentUser() { * @param passwordChars the user's password */ public void handleSignUp(SignUpPageView view, String firstName, String lastName, String email, char[] passwordChars) { - if (firstName == null || firstName.trim().isEmpty() || - lastName == null || lastName.trim().isEmpty() || - email == null || email.trim().isEmpty() || - passwordChars == null || passwordChars.length == 0) { + if (firstName == null || firstName.trim().isEmpty() + || lastName == null || lastName.trim().isEmpty() + || email == null || email.trim().isEmpty() + || passwordChars == null || passwordChars.length == 0) { view.showError("All fields are required"); return; } - BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); - - // Clears password char array after creating a hash. - String hashedPassword = encoder.encode(new String(passwordChars)); - for (int i = 0; i < passwordChars.length; i++) { - passwordChars[i] = '\u0000'; + if (firstName.length() > 32 || lastName.length() > 32 + || email.length() > 32 || passwordChars.length > 72) { + + HashMap> fields = new HashMap>(); + List fields32 = new ArrayList(); + List fields72 = new ArrayList(); + fields.put("32", fields32); + fields.put("72", fields72); + + if (firstName.length() > 32) { + fields32.add("First Name"); + } + if (lastName.length() > 32) { + fields32.add("Last Name"); + } + if (email.length() > 32) { + fields32.add("Email"); + } + if (passwordChars.length > 72) { + fields72.add("Password"); + } + + int length32 = fields.get("32").size(); + int length72 = fields.get("72").size(); + + String string32 = ""; + if (length32 > 0) { + if (length32 > 1) { + for (int i = 0; i < length32; i++) { + if (i == length32 - 1) { + string32 += String.format("and %s", fields.get("32").get(i)); + } else { + string32 += String.format("%s, ", fields.get("32").get(i)); + } + } + string32 = string32 + " must have lengths of 32 characters.\n"; + } else { + string32 = fields.get("32").getFirst() + " must have a length of 32 characters.\n"; + } + } + + String string72 = ""; + if (length72 > 0) { + if (length72 > 1) { + for (int i = 0; i < length72; i++) { + if (i == length72 - 1) { + string72 += String.format("and %s", fields.get("72").get(i)); + } else { + string72 += String.format("%s, ", fields.get("72").get(i)); + } + } + string72 = string72 + " must have lengths of 72 characters.\n"; + } else { + string72 = fields.get("72").getFirst() + + " must have a length of 72 characters.\n"; + } + } + + view.showError(string32 + string72 + "Try again."); + return; } Alert privacyPolicy = new Alert(Alert.AlertType.CONFIRMATION); privacyPolicy.setTitle("Accept Privacy Policy"); privacyPolicy.setHeaderText("Accept Privacy Policy"); privacyPolicy.setContentText( - "Your user information like:\n" + - "Name and email—as well as donations tied to your account—will be saved locally on your machine.\n" + - "This information is only used to create your account, and no data will be sold to third parties.\n" + - "By creating an account, you accept the right of our app to store this information on your computer."); + "Your user information like:\n" + + "Name and email—as well as donations tied to your account—" + + "will be saved locally on your machine.\n" + + "This information is only used to create your account," + + "and no data will be sold to third parties.\n" + + "By creating an account," + + "you accept the right of our app to store this information on your computer."); + + BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); + // Clears password char array after creating a hash. + String hashedPassword = encoder.encode(new String(passwordChars)); + for (int i = 0; i < passwordChars.length; i++) { + passwordChars[i] = '\u0000'; + } if (privacyPolicy.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.OK) { boolean success = userService.registerUser( "Customer", firstName, lastName, email, hashedPassword); if (success) { + User user = userService.getUserByEmail(email); appState.setCurrentUser(user); - nav.showHomePage(); - } else { - view.showError("Registration failed. Email may already be in use."); + nav.showHomePage(); + } else { + view.showError("Registration failed. Email may already be in use."); + } } } -} + /** * Handles the login of a {@link User}. From 58d80d9fd90864f89cfe8f72818d82bfe2b1d549 Mon Sep 17 00:00:00 2001 From: Lucy Ciara Herud-Thomassen <86323303+LucyCiara@users.noreply.github.com> Date: Tue, 21 Apr 2026 21:39:36 +0200 Subject: [PATCH 6/7] fix[SignUpPageView]: add text wrapping to prevent longer error messages from being cut off --- src/main/java/edu/group5/app/view/loginpage/SignUpPageView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 bdcfb54..70f40ea 100644 --- a/src/main/java/edu/group5/app/view/loginpage/SignUpPageView.java +++ b/src/main/java/edu/group5/app/view/loginpage/SignUpPageView.java @@ -66,6 +66,7 @@ public char[] getPassword() { public void showError(String message) { errorLabel.setText(message); + errorLabel.setWrapText(true); errorLabel.setStyle("-fx-text-fill: red;"); } @@ -87,7 +88,6 @@ private VBox getSignUpBox() { private Label getErrorLabel() { errorLabel = new Label(); - errorLabel.setPrefHeight(20); return errorLabel; } From f22ad07c693b102e8d54bceb7251c2c94d098074 Mon Sep 17 00:00:00 2001 From: Lucy Ciara Herud-Thomassen <86323303+LucyCiara@users.noreply.github.com> Date: Tue, 21 Apr 2026 23:46:16 +0200 Subject: [PATCH 7/7] feat[DonationController]: prevent donations that are too large or with too many decimals Add error message when trying to donate an amount of money that has too many digits or decimals. --- src/main/java/edu/group5/app/control/AuthController.java | 2 ++ .../java/edu/group5/app/control/DonationController.java | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/main/java/edu/group5/app/control/AuthController.java b/src/main/java/edu/group5/app/control/AuthController.java index 347a52e..d3d4ebe 100644 --- a/src/main/java/edu/group5/app/control/AuthController.java +++ b/src/main/java/edu/group5/app/control/AuthController.java @@ -86,6 +86,7 @@ public void handleSignUp(SignUpPageView view, String firstName, String lastName, return; } + // Checks if any input is too long. if (firstName.length() > 32 || lastName.length() > 32 || email.length() > 32 || passwordChars.length > 72) { @@ -148,6 +149,7 @@ public void handleSignUp(SignUpPageView view, String firstName, String lastName, return; } + // Privacy policy pop-up. Alert privacyPolicy = new Alert(Alert.AlertType.CONFIRMATION); privacyPolicy.setTitle("Accept Privacy Policy"); privacyPolicy.setHeaderText("Accept Privacy Policy"); diff --git a/src/main/java/edu/group5/app/control/DonationController.java b/src/main/java/edu/group5/app/control/DonationController.java index 07af6fd..3ca392d 100644 --- a/src/main/java/edu/group5/app/control/DonationController.java +++ b/src/main/java/edu/group5/app/control/DonationController.java @@ -178,6 +178,12 @@ private void handleDonate() { return; } + // Prevents donations that are too complex from being made + if (amount.stripTrailingZeros().precision() > 32 || amount.stripTrailingZeros().scale() > 16) { + this.showError("The number is too complex, please donate a smaller or less precise number"); + return; + } + // Create donation via service boolean success = service.donate( customer,