' to create Group";
+ throw new ExpensesException(exceptionMessage);
+ }
+
+ if(params.get("user").isEmpty() || params.get("user").size() > 1){
+ System.out.println("Invalid command. Syntax: settle payerName /user payeeName");
+ return;
+ }
+
+ String payer = argument;
+ String payee = params.get("user").get(0);
+
+ Group.getCurrentGroup().ifPresent(group -> group.settle(payer, payee));
+
+ break;
+ //@@author
+ case "luck":
+ LuckCommand.handleLuck(argument);
+ break;
+ case "list":
+ ListCommand.printList();
+ break;
+ case "balance":
+ BalanceCommand.handleBalance(argument);
+ break;
+ default:
+ System.out.println("That is not a command. " +
+ "Please use one of the commands given here");
+ Help.printHelp();
+ break;
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/duke/Settle.java b/src/main/java/seedu/duke/Settle.java
new file mode 100644
index 0000000000..b46d44289e
--- /dev/null
+++ b/src/main/java/seedu/duke/Settle.java
@@ -0,0 +1,43 @@
+//@@author avrilgk
+package seedu.duke;
+
+
+import java.util.ArrayList;
+
+/**
+ * The Settle class represents a transaction between two users.
+ * It extends the Expense class and has a payer, payee and amount.
+ *
+ * Each Settle object represents a single transaction where one user (the payer)
+ * pays another user (the payee) a certain amount.
+ */
+
+public class Settle extends Expense {
+ private final User payer;
+ private final User payee;
+ private final double amount;
+
+ /**
+ * Constructs a new Settle object.
+ *
+ * @param payer The user who is making the payment
+ * @param payee The user who is receiving the payment
+ * @param amount The amount of the payment
+ */
+ public Settle(User payer, User payee, float amount) {
+ super(payer.getName(), "", new Money(amount , CurrencyConversions.SGD), new ArrayList<>());
+ this.payer = payer;
+ this.payee = payee;
+ this.amount = amount;
+ }
+
+ @Override
+ public String getPayer() {
+ return payer.getName();
+ }
+
+ @Override
+ public String toString() {
+ return payer.getName() + " paid " + payee.getName() + " " + amount;
+ }
+}
diff --git a/src/main/java/seedu/duke/SlotMachine.java b/src/main/java/seedu/duke/SlotMachine.java
new file mode 100644
index 0000000000..3b912d6a9c
--- /dev/null
+++ b/src/main/java/seedu/duke/SlotMachine.java
@@ -0,0 +1,103 @@
+package seedu.duke;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+public class SlotMachine {
+ private static final String slotOutputs = "%^#@$*!~";
+ private List> slotMachine;
+
+ /**
+ * Generates a 3x3 slot machine matrix
+ * Each slot takes up 3 rows and 3 columns
+ *
+ * @param numRows total number of rows
+ * @param numCols total number of columns
+ */
+ SlotMachine(int numRows, int numCols) {
+ // create slot machine matrix
+ slotMachine = new ArrayList<>(numRows);
+ for (int row = 0; row < numRows; row++) {
+ List currentRow = new ArrayList<>(numCols);
+ for (int j = 0; j < numCols; j++) {
+ // Initialize each cell with empty char
+ currentRow.add(' ');
+ }
+ slotMachine.add(currentRow);
+ }
+ // randomise each slot
+ fillSlots();
+ }
+
+ // fill each slot with random characters
+ private void fillSlots() {
+ Random rand = ThreadLocalRandom.current();
+ for (int row = 0; row < slotMachine.size(); row += 3) {
+ for (int col = 0; col < slotMachine.get(0).size(); col += 3) {
+ char randomChar = slotOutputs.charAt(rand.nextInt(slotOutputs.length()));
+ //one char in each slot
+ fillSlot(row, col, randomChar);
+ }
+ }
+ }
+
+ // fill one slot with the same random character
+ private void fillSlot(int startRow, int startCol, char character) {
+ for (int i = startRow; i < startRow + 3; i++) {
+ for (int j = startCol; j < startCol + 3; j++) {
+ slotMachine.get(i).set(j, character);
+ }
+ }
+ }
+
+ // randomise characters
+ void reroll() {
+ fillSlots();
+ }
+
+ // override toString method to print the slot machine
+ @Override
+ public String toString() {
+ StringBuilder matrix = new StringBuilder();
+
+ // Draw the slot machine
+ for (int i = 0; i < slotMachine.size(); i += 3) {
+ // Draw the top of the boxes
+ for (int j = 0; j < slotMachine.get(0).size(); j += 3) {
+ matrix.append("_____");
+ }
+ matrix.append("\n");
+ // Draw the content of each slot
+ for (int row = i; row < i + 3; row++) {
+ for (int j = 0; j < slotMachine.get(0).size(); j += 3) {
+ matrix.append("|");
+ for (int col = j; col < j + 3; col++) {
+ matrix.append(slotMachine.get(row).get(col));
+ }
+ matrix.append("|");
+ }
+ matrix.append("\n");
+ }
+ // Draw the bottom of the boxes
+ for (int j = 0; j < slotMachine.get(0).size(); j += 3) {
+ matrix.append("_____");
+ }
+ matrix.append("\n");
+ }
+ return matrix.toString();
+ }
+ // check if all characters in the middle rows are the same
+ boolean isWin() {
+ for (int i = slotMachine.size() / 2 - 1; i <= slotMachine.size() / 2 + 1; i++) {
+ char firstChar = slotMachine.get(i).get(0);
+ for (int j = 1; j < slotMachine.get(i).size(); j++) {
+ if (slotMachine.get(i).get(j) != firstChar) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/seedu/duke/User.java b/src/main/java/seedu/duke/User.java
new file mode 100644
index 0000000000..01b42dc971
--- /dev/null
+++ b/src/main/java/seedu/duke/User.java
@@ -0,0 +1,13 @@
+package seedu.duke;
+
+public class User {
+ private String userName;
+
+ public User(String userName) {
+ this.userName = userName;
+ }
+
+ public String getName() {
+ return userName;
+ }
+}
diff --git a/src/main/java/seedu/duke/UserInterface.java b/src/main/java/seedu/duke/UserInterface.java
new file mode 100644
index 0000000000..eece804de7
--- /dev/null
+++ b/src/main/java/seedu/duke/UserInterface.java
@@ -0,0 +1,47 @@
+package seedu.duke;
+
+
+public class UserInterface {
+
+ private static final String SUCCESS_BORDER = "<----------SUCCESS----------->";
+ private static final String ERROR_BORDER = "<-----------ERROR------------>";
+ private static final String DEFAULT_BORDER = "<---------------------------->";
+ private static final String HAPPY_CAT =
+ " /\\_/\\\n" +
+ " ( ^.^ )\n" +
+ " > ^ <";
+ private static final String GRUMPY_CAT =
+ " /\\_/\\\n" +
+ " ( >_< )\n" +
+ " > ^ <";
+
+ private static final String SAD_CAT =
+ " /\\_/\\\n" +
+ " ( ._. )\n" +
+ " > ^ <";
+
+ public static void printMessage(String message, MessageType type) {
+ switch (type) {
+ case SUCCESS:
+ System.out.println(HAPPY_CAT);
+ System.out.println(SUCCESS_BORDER);
+ break;
+ case ERROR:
+ System.out.println(GRUMPY_CAT);
+ System.out.println(ERROR_BORDER);
+ break;
+ default:
+ break;
+ }
+
+ System.out.println(message);
+ System.out.println(DEFAULT_BORDER);
+ }
+
+ public static void printMessage(String message) {
+ System.out.println(SAD_CAT);
+ System.out.println(DEFAULT_BORDER);
+ System.out.println(message);
+ System.out.println(DEFAULT_BORDER);
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/BalanceCommand.java b/src/main/java/seedu/duke/commands/BalanceCommand.java
new file mode 100644
index 0000000000..8b6dc20f78
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/BalanceCommand.java
@@ -0,0 +1,36 @@
+package seedu.duke.commands;
+
+import seedu.duke.Balance;
+import seedu.duke.Group;
+import seedu.duke.exceptions.ExpensesException;
+
+import java.util.Optional;
+
+public class BalanceCommand {
+ /**
+ * Checks if user is currently in a Group, and if User specified is in said Group.
+ * Creates a Balance object and prints User's balance if so.
+ *
+ * @param argument The name of User to print the balance of.
+ * @throws ExpensesException If user is not in a Group, or specified User is not in said Group.
+ */
+ public static void handleBalance(String argument) throws ExpensesException{
+ // Checks if user is currently in a Group
+ // named 'currentGroup1' to prevent conflict with previous declaration
+ Optional currentGroup = Group.getCurrentGroup();
+ if (currentGroup.isEmpty()) {
+ String exceptionMessage = "Not signed in to a Group! Use 'create ' to create Group";
+ throw new ExpensesException(exceptionMessage);
+ }
+ assert currentGroup.isPresent() : "Group should be created and present";
+
+ // Checks if user specified is in Current Group
+ if (!currentGroup.get().isMember(argument)) {
+ String exceptionMessage = argument + " is not in current Group!";
+ throw new ExpensesException(exceptionMessage);
+ }
+
+ Balance balance = new Balance(argument, currentGroup.get());
+ balance.printBalance();
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/ExpenseCommand.java b/src/main/java/seedu/duke/commands/ExpenseCommand.java
new file mode 100644
index 0000000000..cbe646622b
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/ExpenseCommand.java
@@ -0,0 +1,278 @@
+package seedu.duke.commands;
+//@@author mukund1403
+
+import seedu.duke.Group;
+import seedu.duke.Pair;
+import seedu.duke.CurrencyConversions;
+import seedu.duke.Money;
+import seedu.duke.Expense;
+import seedu.duke.UserInterface;
+import seedu.duke.exceptions.ExpensesException;
+import seedu.duke.MessageType;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Optional;
+
+public class ExpenseCommand {
+
+ /**
+ * Takes in an expense from Parser and creates a new expense object.
+ * Adds the new expense object into the expense list
+ * @param params : The parameters for the expense including amount, payer, payeeList
+ * @param argument : The description of the expense
+ * @param userInput : A string containing users input
+ * @throws ExpensesException : An exception for expenses class that prints the relevant error message
+ */
+ //@@author Cohii2
+ public static void addExpense(HashMap > params,String argument, String userInput)
+ throws ExpensesException {
+ Optional currentGroup = Group.getCurrentGroup();
+ if (currentGroup.isEmpty()) {
+ throw new ExpensesException("Not signed in to a Group! Use 'create ' to create Group");
+ }
+
+ String[] expenseParams = {"amount", "paid", "user"};
+ for (String expenseParam : expenseParams) {
+ if (params.get(expenseParam).isEmpty()) {
+ throw new ExpensesException("No " + expenseParam + " for expenses! Add /" + expenseParam);
+ }
+ }
+ //@@author mukund1403
+ float totalAmount = getTotal(params);
+ String currencyString;
+ if(params.get("currency").isEmpty()){
+ currencyString = "";
+ } else {
+ currencyString = params.get("currency").get(0);
+ }
+
+ CurrencyConversions currency = getCurrency(currencyString);
+
+ Money amountAndCurrency = new Money(totalAmount, currency);
+ ArrayList payeeList = params.get("user");
+ String payerName = params.get("paid").get(0);
+
+ checkDescription(argument);
+
+ Expense newTransaction;
+ ArrayList> payees = new ArrayList<>();
+ if(userInput.contains("/unequal")){
+ newTransaction = addUnequalExpense(payeeList, payees, amountAndCurrency, payerName, argument);
+ } else {
+ newTransaction = addEqualExpense(payeeList, payees, amountAndCurrency, payerName, argument);
+ }
+ UserInterface.printMessage(newTransaction.successMessageString(),MessageType.SUCCESS);
+ currentGroup.get().addExpense(newTransaction);
+ }
+
+ //@@author mukund1403
+
+ /**
+ * The method deletes expense from the expenses list
+ * @param listIndex : The index from the list, the user wishes to delete (will be 1 indexed)
+ * @throws ExpensesException : An exception for expenses class that prints the relevant error message
+ */
+ public static void deleteExpense(String listIndex) throws ExpensesException {
+ Optional currentGroup = Group.getCurrentGroup();
+ if (currentGroup.isEmpty()) {
+ String exceptionMessage = "Not signed in to a Group! Use 'create ' to create Group";
+ throw new ExpensesException(exceptionMessage);
+ }
+ List expenseList = currentGroup.get().getExpenseList();
+ int listSize = expenseList.size();
+ int index = getListIndex(listIndex, listSize) - 1;
+ String deletedExpenseDescription = expenseList.get(index).toString();
+ currentGroup.get().deleteExpense(index);
+ UserInterface.printMessage("Deleted expense:\n" + deletedExpenseDescription,MessageType.SUCCESS);
+ //System.out.println("Deleted expense:\n" + deletedExpenseDescription);
+ }
+
+ /**
+ * Gets the total amount for the expense
+ * @param params : The parameters for the expense including amount, payer, payeeList
+ * @return : The total amount as a float
+ * @throws ExpensesException : An exception for expenses class that prints the relevant error message
+ */
+ public static Float getTotal(HashMap> params) throws ExpensesException {
+ float totalAmount;
+ String amount = params.get("amount").get(0);
+ try {
+ totalAmount = Float.parseFloat(amount);
+ } catch (NumberFormatException e) {
+ String exceptionMessage = "Re-enter expense with amount as a proper number. (Good bug to start with tbh!)";
+ throw new ExpensesException(exceptionMessage);
+ }
+ int maxNumberHandled = 2000000000;
+ if(totalAmount <= 0){
+ String exceptionMessage = "Expense amount cannot be 0 or a negative number " +
+ "(Can try using special characters. I have not handled that!)";
+ throw new ExpensesException(exceptionMessage);
+ } else if(totalAmount > maxNumberHandled) {
+ String exceptionMessage = "This amount is too big for a small computer like me to handle :(. " +
+ "Please use a smaller amount";
+ throw new ExpensesException(exceptionMessage);
+ }
+ return totalAmount;
+ }
+
+ /**
+ * Checks if the currency entered is valid and returns it
+ * @param currencyString : The currency in String format
+ * @return : The currency for the expense
+ * @throws ExpensesException : An exception for expenses class that prints the relevant error message
+ */
+ public static CurrencyConversions getCurrency(String currencyString)
+ throws ExpensesException {
+ if(currencyString.isEmpty()){
+ return CurrencyConversions.SGD;
+ } else {
+ switch(currencyString.toUpperCase()) {
+ case "USD":
+ return CurrencyConversions.USD;
+ case "RMB":
+ return CurrencyConversions.RMB;
+ case "EUR":
+ return CurrencyConversions.EUR;
+ case "JPY":
+ return CurrencyConversions.JPY;
+ case "AUD":
+ return CurrencyConversions.AUD;
+ case "MYR":
+ return CurrencyConversions.MYR;
+ case "SGD":
+ return CurrencyConversions.SGD;
+ default:
+ throw new ExpensesException("Sorry! Either you have entered the currency name incorrectly or" +
+ " the app does not currently support this currency :(");
+ }
+ }
+ }
+
+ /**
+ * Checks if the expenses list index provided is valid and returns it
+ * @param listIndex : The String containing the list index
+ * @param listSize : The total size of the expenses list
+ * @return : The list index as integer
+ * @throws ExpensesException : An exception for expenses class that prints the relevant error message
+ */
+ public static int getListIndex(String listIndex, int listSize) throws ExpensesException {
+ int index;
+ try{
+ index = Integer.parseInt(listIndex);
+ } catch(NumberFormatException e){
+ String exceptionMessage = "Enter a list index that is an Integer";
+ throw new ExpensesException(exceptionMessage);
+ }
+
+ if(index > listSize){
+ String exceptionMessage = "List index is greater than list size";
+ throw new ExpensesException(exceptionMessage);
+ } else if (index <= 0){
+ String exceptionMessage = "List index cannot be 0 or negative";
+ throw new ExpensesException(exceptionMessage);
+ }
+ return index;
+ }
+
+ /**
+ * Takes in an expense split unequally and creates a new expense
+ * @param payeeList : List of people who owe money for the expense
+ * @param payees : List of people who owe money and how much they owe
+ * @param totalAmountAndCurrency : Money object containing the total amount and the currency of the expense
+ * @param payerName : Name of the person who paid for the expense
+ * @param argument : Description of the expense
+ * @return : The Expense object created
+ * @throws ExpensesException : An exception for expenses class that prints the relevant error message
+ */
+ public static Expense addUnequalExpense(ArrayList payeeList, ArrayList> payees,
+ Money totalAmountAndCurrency,
+ String payerName, String argument) throws ExpensesException{
+ float totalAmount = totalAmountAndCurrency.getAmount();
+ float amountDueByPayees = 0;
+ int payeeInfoMinLength = 2;
+ CurrencyConversions currency = totalAmountAndCurrency.getCurrency();
+ for (String payee : payeeList) {
+ String[] payeeInfo = payee.split(" ");
+
+ if (payeeInfo.length < payeeInfoMinLength) {
+ String exceptionMessage = "Amount due for payee with name "
+ + payeeInfo[0] + " is empty. Enter it and try again";
+ throw new ExpensesException(exceptionMessage);
+ }
+ String payeeName = mergeBack(payeeInfo);
+ checkPayeeInGroup(payeeName);
+ try {
+ float amountDue = Float.parseFloat(payeeInfo[payeeInfo.length - 1]);
+ amountDueByPayees += amountDue;
+ Money amountDueAndCurrency = new Money(amountDue, currency);
+ payees.add(new Pair<>(payeeName, amountDueAndCurrency));
+ } catch (NumberFormatException e) {
+ String exceptionMessage = "Re-enter amount due for payee with name "
+ + payeeName + " as a proper number.";
+ throw new ExpensesException(exceptionMessage);
+ }
+ }
+ if (amountDueByPayees > totalAmount) {
+ String exceptionMessage = "The amount split between users is greater than total amount. Try again.";
+ throw new ExpensesException(exceptionMessage);
+ }
+ float amountDueForPayer = totalAmount - amountDueByPayees;
+ Money amountDueAndCurrency = new Money(amountDueForPayer, currency);
+ payees.add(new Pair<>(payerName, amountDueAndCurrency));
+ return new Expense(payerName, argument, totalAmountAndCurrency, payees);
+ }
+
+
+ /**
+ * Takes in an expense split equally and creates a new expense
+ * @param payeeList : List of people who owe money for the expense
+ * @param payees : List of people who owe money and how much they owe
+ * @param totalAmountAndCurrency : Money object containing the total amount and the currency of the expense
+ * @param payerName : Name of the person who paid for the expense
+ * @param argument : Description of the expense
+ * @return : The Expense object created
+ * @throws ExpensesException : An exception for expenses class that prints the relevant error message
+ */
+ public static Expense addEqualExpense(ArrayList payeeList, ArrayList> payees,
+ Money totalAmountAndCurrency,
+ String payerName,String argument)throws ExpensesException {
+ float totalAmount = totalAmountAndCurrency.getAmount();
+ CurrencyConversions currency = totalAmountAndCurrency.getCurrency();
+ float amountDue = totalAmount / (payeeList.size() + 1);
+ Money amountDueAndCurrency = new Money(amountDue, currency);
+ for (String payee : payeeList) {
+ checkPayeeInGroup(payee);
+ payees.add(new Pair<>(payee, amountDueAndCurrency));
+ }
+ checkPayeeInGroup(payerName);
+ payees.add(new Pair<>(payerName, amountDueAndCurrency));
+ return new Expense(payerName, argument, totalAmountAndCurrency, payees);
+ }
+
+ private static void checkDescription(String argument) throws ExpensesException {
+ if(argument.isEmpty()){
+ System.out.println("Warning! Empty description");
+ } else if(argument.contains("◇")){
+ throw new ExpensesException("Special characters not allowed in description! " +
+ "(Good try trynna catch a bug!)");
+ }
+ }
+
+ private static void checkPayeeInGroup(String payee)
+ throws ExpensesException {
+ if(!Group.isMember(payee)){
+ throw new ExpensesException(payee + " is not a member of the group!");
+ }
+ }
+
+ private static String mergeBack(String[] splitArray){
+ String mergedString = "";
+ for(int i = 0; i < splitArray.length-2; i++){
+ mergedString += splitArray[i].trim() + " ";
+ }
+ mergedString += splitArray[splitArray.length-2];
+ return mergedString;
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/GroupCommand.java b/src/main/java/seedu/duke/commands/GroupCommand.java
new file mode 100644
index 0000000000..300dee97ea
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/GroupCommand.java
@@ -0,0 +1,99 @@
+//@@author hafizuddin-a
+
+package seedu.duke.commands;
+
+import seedu.duke.Group;
+import seedu.duke.exceptions.GroupDeleteException;
+import seedu.duke.storage.FileIO;
+import seedu.duke.storage.FileIOImpl;
+import seedu.duke.storage.GroupNameChecker;
+import seedu.duke.storage.GroupStorage;
+
+import java.util.Optional;
+
+/**
+ * Represents a command handler for group-related operations.
+ * Provides static methods to create groups, add members to groups, and exit groups.
+ */
+public class GroupCommand {
+ /**
+ * Creates a new group or retrieves an existing group with the specified name.
+ *
+ * @param groupName the name of the group to create or retrieve
+ */
+ public static void createGroup(String groupName) {
+ try {
+ Group.getOrCreateGroup(groupName);
+ } catch (IllegalStateException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ //@@author avrilgk
+
+ /**
+ * Deletes the current group.
+ * If the user is not currently in a group, prints a message indicating so.
+ *
+ * @param groupName the name of the group to delete
+ */
+ public static void deleteGroup(String groupName) {
+ Optional currentGroup = Group.getCurrentGroup();
+ if (currentGroup.isPresent() && currentGroup.get().getGroupName().equals(groupName)) {
+ System.out.println("Please exit current group before deleting group.");
+ return;
+ }
+
+ try {
+ FileIO fileIO = new FileIOImpl();
+ GroupStorage groupStorage = new GroupStorage(fileIO);
+ GroupNameChecker groupNameChecker = new GroupNameChecker();
+ if (groupNameChecker.doesGroupNameExist(groupName)) {
+ groupStorage.deleteGroupFile(groupName);
+ Group.removeGroup(groupName);
+ System.out.println("The group " + groupName + " has been deleted.");
+ } else {
+ System.out.println("The group " + groupName + " does not exist.");
+ }
+ } catch (GroupDeleteException e) {
+ System.out.println("Failed to delete the group: " + e.getMessage());
+ }
+ }
+ //@@author hafizuddin-a
+ /**
+ * Adds a member with the specified name to the current group.
+ * If the user is not currently in a group, prints a message asking them to create or join a group first.
+ *
+ * @param memberName the name of the member to add
+ */
+ public static void addMember(String memberName) {
+ Optional currentGroup = Group.getCurrentGroup();
+ if (currentGroup.isEmpty()) {
+ System.out.println("Please create or join a group first.");
+ return;
+ }
+
+ currentGroup.get().addMember(memberName);
+ }
+
+ //@@author avrilgk
+
+ /**
+ * Enters an existing group with the specified name.
+ *
+ * @param groupName the name of the group to enter
+ */
+
+ public static void enterGroup(String groupName) {
+ Group.enterGroup(groupName);
+ }
+
+ /**
+ * Exits the current group.
+ * If the user is not currently in a group, prints a message indicating so.
+ */
+ public static void exitGroup(String groupName) {
+ Group.exitGroup(groupName);
+ }
+}
+
diff --git a/src/main/java/seedu/duke/commands/ListCommand.java b/src/main/java/seedu/duke/commands/ListCommand.java
new file mode 100644
index 0000000000..88e36005fa
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/ListCommand.java
@@ -0,0 +1,25 @@
+package seedu.duke.commands;
+//@@author mukund1403
+import seedu.duke.Expense;
+import seedu.duke.exceptions.ExpensesException;
+import seedu.duke.Group;
+
+import java.util.List;
+import java.util.Optional;
+
+public class ListCommand {
+ public static void printList() throws ExpensesException {
+ Optional currentGroup = Group.getCurrentGroup();
+ if (currentGroup.isEmpty()) {
+ String exceptionMessage = "Not signed in to a Group! Use 'create ' to create Group";
+ throw new ExpensesException(exceptionMessage);
+ }
+ List expenses = currentGroup.get().getExpenseList();
+ System.out.println("The expenses for this group are:\n");
+ int i = 1;
+ for(Expense expense : expenses){
+ System.out.println(i + ". " + expense.toString());
+ i++;
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/commands/LuckCommand.java b/src/main/java/seedu/duke/commands/LuckCommand.java
new file mode 100644
index 0000000000..1a75cbe836
--- /dev/null
+++ b/src/main/java/seedu/duke/commands/LuckCommand.java
@@ -0,0 +1,35 @@
+package seedu.duke.commands;
+
+import seedu.duke.Group;
+import seedu.duke.Luck;
+import seedu.duke.exceptions.ExpensesException;
+import seedu.duke.exceptions.LuckException;
+
+import java.util.Optional;
+
+public class LuckCommand {
+ /**
+ * Checks if user is currently in a Group
+ * Checks if the group has more than one person.
+ * Creates a Luck object
+ * Prints the welcome message and start the slot machine.
+ * @throws LuckException If user is not in a Group
+ * @throws LuckException If the group has less than 2 people.
+ */
+ public static void handleLuck(String argument) throws LuckException, ExpensesException {
+ // Checks if user is currently in a Group
+ Optional currentGroup = Group.getCurrentGroup();
+ if (currentGroup.isEmpty()) {
+ String exceptionMessage = "Not signed in to a Group! Use 'create ' to create Group";
+ throw new LuckException(exceptionMessage);
+ }
+ Group selectedGroup = currentGroup.get();
+ if (selectedGroup.getMembers().size() <= 1){
+ String exceptionMessage = "You need more people to get lucky!!!";
+ throw new LuckException(exceptionMessage);
+ }
+ Luck newLuck = new Luck(selectedGroup, argument);
+ newLuck.printWelcome();
+ newLuck.startGambling();
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/ExpensesException.java b/src/main/java/seedu/duke/exceptions/ExpensesException.java
new file mode 100644
index 0000000000..0fd159561e
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/ExpensesException.java
@@ -0,0 +1,15 @@
+package seedu.duke.exceptions;
+
+public class ExpensesException extends Exception {
+ public ExpensesException(String s, Throwable err){
+ super(s,err);
+ }
+
+ public ExpensesException(String s){
+ super(s);
+ }
+
+ public ExpensesException() {
+
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/GroupDeleteException.java b/src/main/java/seedu/duke/exceptions/GroupDeleteException.java
new file mode 100644
index 0000000000..89f7c224b3
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/GroupDeleteException.java
@@ -0,0 +1,16 @@
+package seedu.duke.exceptions;
+
+/**
+ * Represents an exception that occurs during the deletion of a group.
+ * This exception is thrown when there is an error or failure in the group deletion process.
+ */
+public class GroupDeleteException extends UniversalExceptions {
+ /**
+ * Constructs a new GroupDeleteException with the specified detail message.
+ *
+ * @param message the detail message describing the exception
+ */
+ public GroupDeleteException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/GroupLoadException.java b/src/main/java/seedu/duke/exceptions/GroupLoadException.java
new file mode 100644
index 0000000000..389a2ad48f
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/GroupLoadException.java
@@ -0,0 +1,16 @@
+package seedu.duke.exceptions;
+
+/**
+ * Represents an exception that occurs when loading group information fails.
+ * This exception is thrown when an error occurs while loading a group from a file.
+ */
+public class GroupLoadException extends UniversalExceptions {
+ /**
+ * Constructs a new GroupLoadException with the specified detail message.
+ *
+ * @param message The detail message.
+ */
+ public GroupLoadException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/GroupSaveException.java b/src/main/java/seedu/duke/exceptions/GroupSaveException.java
new file mode 100644
index 0000000000..ea739307b5
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/GroupSaveException.java
@@ -0,0 +1,16 @@
+package seedu.duke.exceptions;
+
+/**
+ * Represents an exception that occurs when saving group information fails.
+ * This exception is thrown when an error occurs while saving a group to a file.
+ */
+public class GroupSaveException extends UniversalExceptions {
+ /**
+ * Constructs a new GroupSaveException with the specified detail message.
+ *
+ * @param message The detail message.
+ */
+ public GroupSaveException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/LuckException.java b/src/main/java/seedu/duke/exceptions/LuckException.java
new file mode 100644
index 0000000000..c160606e40
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/LuckException.java
@@ -0,0 +1,7 @@
+package seedu.duke.exceptions;
+
+public class LuckException extends UniversalExceptions {
+ public LuckException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/seedu/duke/exceptions/UniversalExceptions.java b/src/main/java/seedu/duke/exceptions/UniversalExceptions.java
new file mode 100644
index 0000000000..445a81526c
--- /dev/null
+++ b/src/main/java/seedu/duke/exceptions/UniversalExceptions.java
@@ -0,0 +1,17 @@
+package seedu.duke.exceptions;
+
+public class UniversalExceptions extends Exception {
+ private final String errorMessage;
+ UniversalExceptions(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ String getErrorMessage() {
+ return this.errorMessage;
+ }
+
+ @Override
+ public String toString() {
+ return this.errorMessage;
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/FileIO.java b/src/main/java/seedu/duke/storage/FileIO.java
new file mode 100644
index 0000000000..53770ddfe9
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/FileIO.java
@@ -0,0 +1,38 @@
+package seedu.duke.storage;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+
+/**
+ * Represents the file I/O operations.
+ * Defines methods for reading from and writing to files.
+ */
+public interface FileIO {
+ /**
+ * Returns a BufferedReader for reading from the specified file.
+ *
+ * @param filePath The path of the file to read from.
+ * @return A BufferedReader for reading from the file.
+ * @throws IOException If an I/O error occurs while creating the reader.
+ */
+ BufferedReader getFileReader(String filePath) throws IOException;
+
+ /**
+ * Returns a BufferedWriter for writing to the specified file.
+ *
+ * @param filePath The path of the file to write to.
+ * @return A BufferedWriter for writing to the file.
+ * @throws IOException If an I/O error occurs while creating the writer.
+ */
+ BufferedWriter getFileWriter(String filePath) throws IOException;
+
+ /**
+ * Deletes the file at the specified file path.
+ *
+ * @param filePath The path of the file to be deleted.
+ * @return true if the file was successfully deleted, false otherwise.
+ * @throws IOException If an I/O error occurs while deleting the file.
+ */
+ boolean deleteFile(String filePath) throws IOException;
+}
diff --git a/src/main/java/seedu/duke/storage/FileIOImpl.java b/src/main/java/seedu/duke/storage/FileIOImpl.java
new file mode 100644
index 0000000000..1b7bb9a165
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/FileIOImpl.java
@@ -0,0 +1,52 @@
+package seedu.duke.storage;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+
+/**
+ * Implements the FileIO interface.
+ * Provides concrete implementations for file I/O operations using BufferedReader and BufferedWriter.
+ */
+public class FileIOImpl implements FileIO {
+ /**
+ * Returns a BufferedReader for reading from the specified file.
+ *
+ * @param filePath The path of the file to read from.
+ * @return A BufferedReader for reading from the file.
+ * @throws IOException If an I/O error occurs while creating the reader.
+ */
+ @Override
+ public BufferedReader getFileReader(String filePath) throws IOException {
+ return new BufferedReader(new FileReader(filePath));
+ }
+
+ /**
+ * Returns a BufferedWriter for writing to the specified file.
+ *
+ * @param filePath The path of the file to write to.
+ * @return A BufferedWriter for writing to the file.
+ * @throws IOException If an I/O error occurs while creating the writer.
+ */
+ @Override
+ public BufferedWriter getFileWriter(String filePath) throws IOException {
+ return new BufferedWriter(new FileWriter(filePath));
+ }
+
+ /**
+ * Deletes the file at the specified file path.
+ *
+ * @param filePath The path of the file to be deleted.
+ * @return true if the file was successfully deleted, false otherwise.
+ * @throws IOException If an I/O error occurs while deleting the file.
+ */
+ @Override
+ public boolean deleteFile(String filePath) throws IOException {
+ File file = new File(filePath);
+ return file.delete();
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/GroupFilePath.java b/src/main/java/seedu/duke/storage/GroupFilePath.java
new file mode 100644
index 0000000000..858eb1afe7
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/GroupFilePath.java
@@ -0,0 +1,59 @@
+package seedu.duke.storage;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Represents the file path management for group files.
+ * Provides methods to get the file path for a group and create the groups directory.
+ */
+public class GroupFilePath {
+ private static String groupsDirectory = "data/groups";
+ private static final String GROUP_FILE_EXTENSION = ".txt";
+
+ /**
+ * Returns the file path for the group file.
+ *
+ * @param groupName The name of the group.
+ * @return The file path for the group file.
+ */
+ public static String getFilePath(String groupName) {
+ assert groupName != null && !groupName.isEmpty() : "Group name cannot be null or empty";
+ return groupsDirectory + "/" + groupName + GROUP_FILE_EXTENSION;
+ }
+
+ /**
+ * Creates the groups directory if it does not exist.
+ *
+ * @throws IOException If an I/O error occurs while creating the directory.
+ */
+ public static void createGroupDirectory() throws IOException {
+ Path path = Paths.get(groupsDirectory);
+ if (!Files.exists(path)) {
+ Files.createDirectories(path);
+ }
+ }
+
+ /**
+ * Sets the directory where group files are stored.
+ *
+ * This method allows changing the default groups directory to a custom directory.
+ * It is useful for testing purposes or when the groups need to be stored in a different location.
+ *
+ * Assertions are used to check that the provided directory is not null or empty.
+ * If the assertion fails, an {@code AssertionError} will be thrown, indicating a programming error.
+ *
+ * @param directory the directory where group files should be stored
+ * @throws AssertionError if the provided directory is null or empty
+ */
+ public static void setGroupsDirectory(String directory) {
+ assert directory != null && !directory.isEmpty() : "Groups directory cannot be null or empty";
+ groupsDirectory = directory;
+ }
+
+ public static String getGroupsDirectory() {
+ return groupsDirectory;
+ }
+}
diff --git a/src/main/java/seedu/duke/storage/GroupNameChecker.java b/src/main/java/seedu/duke/storage/GroupNameChecker.java
new file mode 100644
index 0000000000..86b64812a1
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/GroupNameChecker.java
@@ -0,0 +1,75 @@
+package seedu.duke.storage;
+
+import seedu.duke.MessageType;
+import seedu.duke.UserInterface;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class GroupNameChecker {
+
+ /**
+ * Checks if a specific group name exists in the saved files.
+ *
+ * @param groupNameToCheck the group name to check for
+ * @return true if the group name exists, false otherwise
+ */
+ public boolean doesGroupNameExist(String groupNameToCheck) {
+ Path groupsDirectory = getGroupsDirectoryPath();
+ if (groupsDirectory == null) {
+ return false;
+ }
+ return checkGroupNameInDirectory(groupsDirectory, groupNameToCheck);
+ }
+
+ /**
+ * Gets the Path object for the groups directory.
+ *
+ * @return the Path object for the groups directory, or null if an error occurs
+ */
+ private Path getGroupsDirectoryPath() {
+ try {
+ return Paths.get(GroupFilePath.getGroupsDirectory());
+ } catch (Exception e) {
+ UserInterface.printMessage("An error occurred while getting the groups directory path: "
+ + e.getMessage(), MessageType.ERROR);
+ return null;
+ }
+ }
+
+ /**
+ * Checks if the given group name exists within the specified directory.
+ *
+ * @param directoryPath the path to the directory containing group files
+ * @param groupNameToCheck the group name to check for
+ * @return true if the group name exists, false otherwise
+ */
+ private boolean checkGroupNameInDirectory(Path directoryPath, String groupNameToCheck) {
+ try (DirectoryStream stream = Files.newDirectoryStream(directoryPath, "*.txt")) {
+ for (Path file : stream) {
+ if (extractGroupNameFromFile(file).equals(groupNameToCheck)) {
+ return true;
+ }
+ }
+ } catch (IOException e) {
+ System.out.println("Data directory not found: " + e.getMessage() + ". Creating new directory...");
+ }
+ return false;
+ }
+
+ /**
+ * Extracts the group name from a file path.
+ *
+ * @param file the Path object of the file
+ * @return the extracted group name
+ */
+ private String extractGroupNameFromFile(Path file) {
+ String fileName = file.getFileName().toString();
+ return fileName.substring(0, fileName.lastIndexOf('.'));
+ }
+}
+
+
diff --git a/src/main/java/seedu/duke/storage/GroupStorage.java b/src/main/java/seedu/duke/storage/GroupStorage.java
new file mode 100644
index 0000000000..f6434a4c68
--- /dev/null
+++ b/src/main/java/seedu/duke/storage/GroupStorage.java
@@ -0,0 +1,247 @@
+package seedu.duke.storage;
+
+import seedu.duke.CurrencyConversions;
+import seedu.duke.Expense;
+import seedu.duke.Group;
+import seedu.duke.MessageType;
+import seedu.duke.Money;
+import seedu.duke.Pair;
+import seedu.duke.User;
+import seedu.duke.UserInterface;
+import seedu.duke.commands.ExpenseCommand;
+import seedu.duke.exceptions.ExpensesException;
+import seedu.duke.exceptions.GroupDeleteException;
+import seedu.duke.exceptions.GroupLoadException;
+import seedu.duke.exceptions.GroupSaveException;
+
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents the storage manager for group data.
+ * Handles the saving and loading of group information to and from files.
+ */
+public class GroupStorage {
+ public static boolean isLoading = false;
+
+ private static final String MEMBERS_HEADER = "Members:";
+ private static final String EXPENSES_HEADER = "Expenses:";
+ private static final String EXPENSE_DELIMITER = "#";
+ private static final String PAYEE_DELIMITER = ":";
+ private static final String PAYEE_DATA_DELIMITER = ",";
+
+ private final FileIO fileIO;
+
+ /**
+ * Constructs a GroupStorage object with the specified FileIO dependency.
+ *
+ * @param fileIO the FileIO instance for file input/output operations
+ */
+ public GroupStorage(FileIO fileIO) {
+ this.fileIO = fileIO;
+ }
+
+ /**
+ * Saves the group information to a file.
+ *
+ * @param group the group to save
+ * @throws GroupSaveException if an error occurs while saving the group information
+ */
+ public void saveGroupToFile(Group group) throws GroupSaveException {
+ try {
+ GroupFilePath.createGroupDirectory();
+ String filePath = GroupFilePath.getFilePath(group.getGroupName());
+ BufferedWriter writer = fileIO.getFileWriter(filePath);
+
+ saveGroupName(writer, group.getGroupName());
+ saveMembers(writer, group.getMembers());
+ saveExpenses(writer, group.getExpenseList());
+
+ writer.close();
+ } catch (IOException e) {
+ throw new GroupSaveException("An error occurred while saving the group information.");
+ }
+ }
+
+ /**
+ * Saves the group name to the file.
+ *
+ * @param writer the BufferedWriter for writing to the file
+ * @param groupName the name of the group
+ * @throws IOException if an I/O error occurs while writing to the file
+ */
+ private void saveGroupName(BufferedWriter writer, String groupName) throws IOException {
+ writer.write(groupName);
+ writer.newLine();
+ }
+
+ /**
+ * Saves the group members to the file.
+ *
+ * @param writer the BufferedWriter for writing to the file
+ * @param members the list of members in the group
+ * @throws IOException if an I/O error occurs while writing to the file
+ */
+ private void saveMembers(BufferedWriter writer, List members) throws IOException {
+ writer.write(MEMBERS_HEADER);
+ writer.newLine();
+ for (User member : members) {
+ writer.write(member.getName());
+ writer.newLine();
+ }
+ }
+
+ /**
+ * Saves the group expenses to the file.
+ *
+ * @param writer the BufferedWriter for writing to the file
+ * @param expenses the list of expenses in the group
+ * @throws IOException if an I/O error occurs while writing to the file
+ */
+ private void saveExpenses(BufferedWriter writer, List expenses) throws IOException {
+ writer.write(EXPENSES_HEADER);
+ writer.newLine();
+ for (Expense expense : expenses) {
+ StringBuilder expenseData = new StringBuilder();
+ expenseData.append(expense.getTotalAmount()).append(EXPENSE_DELIMITER)
+ .append(expense.getCurrency()).append(EXPENSE_DELIMITER)
+ .append(expense.getPayerName()).append(EXPENSE_DELIMITER)
+ .append(expense.getDescription()).append(EXPENSE_DELIMITER);
+
+ List payeeData = new ArrayList<>();
+ for (Pair payee : expense.getPayees()) {
+ payeeData.add(payee.getKey() + PAYEE_DELIMITER + payee.getValue().getAmount()
+ + PAYEE_DELIMITER + payee.getValue().getCurrency());
+ }
+ expenseData.append(String.join(PAYEE_DATA_DELIMITER, payeeData));
+
+ writer.write(expenseData.toString());
+ writer.newLine();
+ }
+ }
+
+ /**
+ * Loads the group information from a file.
+ *
+ * @param groupName the name of the group to load
+ * @return the loaded group
+ * @throws GroupLoadException if an error occurs while loading the group information
+ */
+ public Group loadGroupFromFile(String groupName) throws GroupLoadException {
+ isLoading = true;
+ try {
+ String filePath = GroupFilePath.getFilePath(groupName);
+ BufferedReader reader = fileIO.getFileReader(filePath);
+
+ Group group = loadGroupName(reader);
+ if (group == null) {
+ throw new GroupLoadException("Invalid group data file. Unable to load group name.");
+ }
+
+ try {
+ loadMembers(reader, group);
+ loadExpenses(reader, group);
+ } catch (IOException e) {
+ throw new GroupLoadException("Error loading group members or expenses: " + e.getMessage());
+ }
+
+ reader.close();
+ return group;
+ } catch (IOException e) {
+ throw new GroupLoadException("An error occurred while loading the group: " + e.getMessage());
+ } catch (Exception e) {
+ throw new GroupLoadException("An unexpected error occurred while loading the group: " + e.getMessage());
+ } finally {
+ isLoading = false;
+ UserInterface.printMessage("Group loaded successfully.", MessageType.SUCCESS);
+ }
+ }
+
+ /**
+ * Loads the group name from the file.
+ *
+ * @param reader the BufferedReader for reading from the file
+ * @return the loaded group
+ * @throws IOException if an I/O error occurs while reading from the file
+ */
+ private Group loadGroupName(BufferedReader reader) throws IOException {
+ String line = reader.readLine();
+ if (line != null && !line.isEmpty()) {
+ return new Group(line.trim());
+ }
+ return null;
+ }
+
+ /**
+ * Loads the group members from the file.
+ *
+ * @param reader the BufferedReader for reading from the file
+ * @param group the group to add the loaded members to
+ * @throws IOException if an I/O error occurs while reading from the file
+ */
+ private void loadMembers(BufferedReader reader, Group group) throws IOException {
+ String header = reader.readLine();
+ if (header == null || !header.equals(MEMBERS_HEADER)) {
+ throw new IOException("Invalid group data file. Missing or invalid 'Members:' header.");
+ }
+
+ String line;
+ while ((line = reader.readLine()) != null && !line.equals(EXPENSES_HEADER)) {
+ group.addMember(line);
+ }
+ }
+
+ /**
+ * Loads the group expenses from the file.
+ *
+ * @param reader the BufferedReader for reading from the file
+ * @param group the group to add the loaded expenses to
+ * @throws IOException if an I/O error occurs while reading from the file
+ */
+ private void loadExpenses(BufferedReader reader, Group group) throws IOException, ExpensesException {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] expenseData = line.split(EXPENSE_DELIMITER, 5);
+ float totalAmount = Float.parseFloat(expenseData[0]);
+ String currencyString = expenseData[1];
+ String payerName = expenseData[2];
+ String description = expenseData[3];
+ String[] payeeData = expenseData[4].split(PAYEE_DATA_DELIMITER);
+
+ CurrencyConversions currency = ExpenseCommand.getCurrency(currencyString);
+ ArrayList> payeeList = new ArrayList<>();
+ for (String payee : payeeData) {
+ String[] payeeInfo = payee.split(PAYEE_DELIMITER);
+ String payeeName = payeeInfo[0];
+ float amountDue = Float.parseFloat(payeeInfo[1]);
+ Money amountAndCurrency = new Money(amountDue, currency);
+ payeeList.add(new Pair<>(payeeName, amountAndCurrency));
+ }
+ Money amountAndCurrency = new Money(totalAmount, currency);
+ Expense expense = new Expense(payerName, description, amountAndCurrency, payeeList);
+ group.addExpense(expense);
+ }
+ }
+
+ /**
+ * Deletes the group file for the specified group name.
+ *
+ * @param groupName the name of the group whose file needs to be deleted
+ * @throws GroupDeleteException if an error occurs while deleting the group file
+ */
+ public void deleteGroupFile(String groupName) throws GroupDeleteException {
+ try {
+ String filePath = GroupFilePath.getFilePath(groupName);
+ boolean deleted = fileIO.deleteFile(filePath);
+ if (!deleted) {
+ throw new GroupDeleteException("Failed to delete the group file.");
+ }
+ } catch (IOException e) {
+ throw new GroupDeleteException("An error occurred while deleting the group file: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/test/java/seedu/duke/AddUserTest.java b/src/test/java/seedu/duke/AddUserTest.java
new file mode 100644
index 0000000000..5298c9137c
--- /dev/null
+++ b/src/test/java/seedu/duke/AddUserTest.java
@@ -0,0 +1,38 @@
+package seedu.duke;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class AddUserTest {
+
+ @Test
+ public void testUser() {
+ try {
+ User user = new User("John");
+ assertEquals("John", user.getName());
+ } catch (Exception e) {
+ fail("Exception occurred while creating a User object: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testAddUserToGroup() {
+ String groupName = "TestGroup";
+ Optional group = Group.getOrCreateGroup(groupName);
+ if (group.isEmpty()) {
+ System.out.println("Group does not exist.");
+ return;
+ }
+
+ User user = group.get().addMember("TestUser");
+
+ assertTrue(group.get().getMembers().contains(user), "User was not added to the group");
+ }
+}
+
+
diff --git a/src/test/java/seedu/duke/BalanceTest.java b/src/test/java/seedu/duke/BalanceTest.java
new file mode 100644
index 0000000000..36e3e4e6e5
--- /dev/null
+++ b/src/test/java/seedu/duke/BalanceTest.java
@@ -0,0 +1,85 @@
+package seedu.duke;
+
+import org.junit.jupiter.api.Test;
+import seedu.duke.exceptions.ExpensesException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+//@@author Cohii2
+public class BalanceTest {
+ @Test
+ public void testConstructor() throws ExpensesException {
+ List users = new ArrayList<>();
+ users.add(new User("member1"));
+ users.add(new User("member2"));
+ users.add(new User("member3"));
+
+ List expenses = new ArrayList<>();
+ expenses.add(new Expense("member1", "expense1",
+ new Money(20f, CurrencyConversions.SGD),
+ new ArrayList<>(Arrays.asList(
+ new Pair<>("member2", new Money(5.0f, CurrencyConversions.SGD)),
+ new Pair<>("member3", new Money(10.0f, CurrencyConversions.SGD))
+ ))));
+ expenses.add(new Expense("member2", "expense2",
+ new Money(30f, CurrencyConversions.SGD),
+ new ArrayList<>(Arrays.asList(
+ new Pair<>("member1", new Money(10.0f, CurrencyConversions.SGD)),
+ new Pair<>("member3", new Money(10.0f, CurrencyConversions.SGD))
+ ))));
+ expenses.add(new Expense("member3", "expense3",
+ new Money(100f, CurrencyConversions.AUD),
+ new ArrayList<>(Arrays.asList(
+ new Pair<>("member1", new Money(50.0f, CurrencyConversions.AUD))
+ ))));
+
+ Balance member1Balance = new Balance("member1", expenses, users);
+ member1Balance.printBalance();
+ Balance member2Balance = new Balance("member2", expenses, users);
+ member2Balance.printBalance();
+ Balance member3Balance = new Balance("member3", expenses, users);
+ member3Balance.printBalance();
+
+ testList(moneyListMaker(new Money(-5.00f, CurrencyConversions.SGD)),
+ member1Balance.getBalanceList().get("member2"));
+ testList(moneyListMaker(new Money(10.0f, CurrencyConversions.SGD),
+ new Money(-50.0f, CurrencyConversions.AUD)),
+ member1Balance.getBalanceList().get("member3"));
+
+ testList(moneyListMaker(new Money(5.0f, CurrencyConversions.SGD)),
+ member2Balance.getBalanceList().get("member1"));
+ testList(moneyListMaker(new Money(10.0f, CurrencyConversions.SGD)),
+ member2Balance.getBalanceList().get("member3"));
+
+ testList(moneyListMaker(new Money(-10.0f, CurrencyConversions.SGD)),
+ member3Balance.getBalanceList().get("member2"));
+ testList(moneyListMaker(new Money(-10.0f, CurrencyConversions.SGD),
+ new Money(50.0f, CurrencyConversions.AUD)),
+ member3Balance.getBalanceList().get("member1"));
+ }
+
+ public void testList(List l1, List l2){
+ assert(l1.size() == l2.size());
+ for(int i = 0; i < l1.size(); i++){
+ assertEquals(l1.get(i).getCurrency(), l2.get(i).getCurrency());
+ assertEquals(l1.get(i).getAmount(), l2.get(i).getAmount());
+ }
+ }
+
+ public List moneyListMaker(Money money1, Money money2){
+ List moneyList = new ArrayList<>();
+ moneyList.add(money1);
+ moneyList.add(money2);
+ return moneyList;
+ }
+
+ public List moneyListMaker(Money money1){
+ List moneyList = new ArrayList<>();
+ moneyList.add(money1);
+ return moneyList;
+ }
+}
diff --git a/src/test/java/seedu/duke/CurrencyConversionsTest.java b/src/test/java/seedu/duke/CurrencyConversionsTest.java
new file mode 100644
index 0000000000..3833753e36
--- /dev/null
+++ b/src/test/java/seedu/duke/CurrencyConversionsTest.java
@@ -0,0 +1,24 @@
+package seedu.duke;
+
+import org.junit.jupiter.api.Test;
+
+public class CurrencyConversionsTest {
+ @Test
+ void testCurrencyConversions() {
+ assert (CurrencyConversions.AUD.getName().equals("AUD"));
+ assert (CurrencyConversions.AUD.getRate() == 1.12F);
+ assert (CurrencyConversions.AUD.getInverseRate() == 1.00F / 1.12F);
+ assert (CurrencyConversions.USD.getName().equals("USD"));
+ assert (CurrencyConversions.USD.getRate() == 0.74F);
+ assert (CurrencyConversions.RMB.getName().equals("RMB"));
+ assert (CurrencyConversions.RMB.getRate() == 5.35F);
+ assert (CurrencyConversions.EUR.getName().equals("EUR"));
+ assert (CurrencyConversions.EUR.getRate() == 0.687F);
+ assert (CurrencyConversions.JPY.getName().equals("JPY"));
+ assert (CurrencyConversions.JPY.getRate() == 112.12F);
+ assert (CurrencyConversions.MYR.getName().equals("MYR"));
+ assert (CurrencyConversions.MYR.getRate() == 3.50F);
+ assert (CurrencyConversions.SGD.getName().equals("SGD"));
+ assert (CurrencyConversions.SGD.getRate() == 1.00F);
+ }
+}
diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/duke/DukeTest.java
index 2dda5fd651..f3d4ed2e55 100644
--- a/src/test/java/seedu/duke/DukeTest.java
+++ b/src/test/java/seedu/duke/DukeTest.java
@@ -1,12 +1,10 @@
package seedu.duke;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
import org.junit.jupiter.api.Test;
-class DukeTest {
+public class DukeTest {
@Test
- public void sampleTest() {
- assertTrue(true);
+ public void groupTest() {
+ // test code here
}
}
diff --git a/src/test/java/seedu/duke/ExpenseTest.java b/src/test/java/seedu/duke/ExpenseTest.java
new file mode 100644
index 0000000000..f663cfa1e1
--- /dev/null
+++ b/src/test/java/seedu/duke/ExpenseTest.java
@@ -0,0 +1,109 @@
+package seedu.duke;
+//@@author mukund1403
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import seedu.duke.commands.ExpenseCommand;
+import seedu.duke.exceptions.ExpensesException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+class ExpenseTest{
+ @Test
+ void newExpenseTest() {
+ ArrayList> payees = new ArrayList<>(Arrays.asList(
+ new Pair<>("cohii", new Money(2.0f, CurrencyConversions.SGD)),
+ new Pair<>("shao", new Money(3.2f, CurrencyConversions.SGD)),
+ new Pair<>("avril", new Money(1.0f, CurrencyConversions.SGD)),
+ new Pair<>("hafiz", new Money(2.0f, CurrencyConversions.SGD)),
+ new Pair<>("mukund", new Money(1.8f, CurrencyConversions.SGD))
+ ));
+
+ Money totalAmountAndCurrency = new Money(10,CurrencyConversions.SGD);
+ Expense testExpense1 = new Expense("mukund","disneyland",
+ totalAmountAndCurrency, payees);
+
+ assertEquals(testExpense1.getPayees(),payees);
+ }
+
+ @Test
+ public void amountNotFloatTest() {
+ ArrayList amountArrayList = new ArrayList<>();
+ amountArrayList.add("b");
+ HashMap> params = new HashMap<>();
+ params.put("amount",amountArrayList);
+ Exception e = Assertions.assertThrows(ExpensesException.class,
+ () -> ExpenseCommand.getTotal(params),
+ "Function should throw Expenses exception since exception occured in expenses class.");
+ assertEquals("Re-enter expense with amount as a proper number. " +
+ "(Good bug to start with tbh!)", e.getMessage(),
+ "Exception message should indicate that amount entered was not a number");
+
+ }
+
+ @Test
+ public void listIndexGreaterTest(){
+ String listIndex = "20";
+ int listSize = 2;
+ Exception e = Assertions.assertThrows(ExpensesException.class,
+ () -> ExpenseCommand.getListIndex(listIndex, listSize),
+ "Function should throw ExpensesException");
+ assertEquals("List index is greater than list size",e.getMessage(),
+ "Exception message should indicate that listIndex entered was greater than list size.");
+ }
+
+ @Test
+ public void listIndexNegativeTest(){
+ String listIndex = "-1";
+ int listSize = 2;
+ Exception e = Assertions.assertThrows(ExpensesException.class,
+ () -> ExpenseCommand.getListIndex(listIndex, listSize),
+ "Function should throw ExpensesException");
+ assertEquals("List index cannot be 0 or negative",e.getMessage(),
+ "Exception message should indicate that listIndex entered was less than or equal to 0.");
+ }
+
+ @Test
+ public void listIndexNotIntegerTest(){
+ String listIndex1 = "a";
+ int listSize = 2;
+ Exception e1 = Assertions.assertThrows(ExpensesException.class,
+ () -> ExpenseCommand.getListIndex(listIndex1, listSize),
+ "Function should throw ExpensesException");
+ assertEquals("Enter a list index that is an Integer",e1.getMessage(),
+ "Exception message should indicate that listIndex entered was not an integer.");
+
+ String listIndex2 = "5.0";
+ Exception e2 = Assertions.assertThrows(ExpensesException.class,
+ () -> ExpenseCommand.getListIndex(listIndex1, listSize),
+ "Function should throw ExpensesException");
+ assertEquals("Enter a list index that is an Integer",e2.getMessage(),
+ "Exception message should indicate that listIndex entered was not an integer.");
+ }
+
+ @Test
+ public void unequalExpenseTest() {
+ ArrayList payeeList = new ArrayList<>(Arrays.asList(
+ ("cohii"),
+ ("shao 1"),
+ ("avril 5.5"),
+ ("hafiz"),
+ ("mukund 2")
+ ));
+ ArrayList> payees = new ArrayList<>();
+ Money totalAmount = new Money(10,CurrencyConversions.SGD);
+ String payerName = "mukund";
+ String argument = "disneyland";
+ Exception e = Assertions.assertThrows(ExpensesException.class,
+ () -> ExpenseCommand.addUnequalExpense(payeeList, payees, totalAmount, payerName, argument),
+ "Function should throw ExpensesException");
+ assertEquals("Amount due for payee with name cohii" +
+ " is empty. Enter it and try again", e.getMessage(),
+ "Exception message should indicate that amount for user has not been entered");
+ }
+}
diff --git a/src/test/java/seedu/duke/GroupTest.java b/src/test/java/seedu/duke/GroupTest.java
new file mode 100644
index 0000000000..847662018b
--- /dev/null
+++ b/src/test/java/seedu/duke/GroupTest.java
@@ -0,0 +1,74 @@
+//@@author avrilgk
+
+package seedu.duke;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class GroupTest {
+ @BeforeEach
+ public void setup() {
+ // Clear all groups and reset the current group name before each test
+ Group.groups.clear();
+ Group.currentGroupName = Optional.empty();
+ }
+
+ @Test
+ public void testGetOrCreateGroup() {
+ String groupName = "TestGroup";
+ Group.getOrCreateGroup(groupName);
+ assertTrue(Group.groups.containsKey(groupName), "Group should be created");
+ }
+
+ @Test
+ public void testEnterGroup() {
+ String groupName = "TestGroup";
+ Group.getOrCreateGroup(groupName);
+ Group.enterGroup(groupName);
+ assertEquals(groupName, Group.currentGroupName.get(), "Current group name should match the expected name");
+ }
+
+ @Test
+ public void testExitGroup() {
+ String groupName = "TestGroup";
+ Group.getOrCreateGroup(groupName);
+ Group.enterGroup(groupName);
+ Group.exitGroup(groupName);
+ assertTrue(Group.currentGroupName.isEmpty(), "Current group name should be empty");
+ }
+
+ @Test
+ public void testNullGroup() {
+ String groupName = "TestGroup";
+ Group.getOrCreateGroup(groupName);
+ Group.enterGroup(groupName);
+ Group.exitGroup(groupName);
+ Group.exitGroup(groupName);
+ assertTrue(Group.currentGroupName.isEmpty(), "Current group name should be empty");
+ }
+
+ @Test
+ public void testAddMember() {
+ String groupName = "TestGroup";
+ Group.getOrCreateGroup(groupName);
+ Group.enterGroup(groupName);
+ Group.getCurrentGroup().get().addMember("Alice");
+ assertTrue(Group.getCurrentGroup().get().isMember("Alice"), "Alice should be a member of the group");
+ }
+
+ @Test
+ public void testAddMultipleMembers() {
+ String groupName = "TestGroup";
+ Group.getOrCreateGroup(groupName);
+ Group.enterGroup(groupName);
+ Group.getCurrentGroup().get().addMember("Alice");
+ Group.getCurrentGroup().get().addMember("Bob");
+ assertTrue(Group.getCurrentGroup().get().isMember("Alice"), "Alice should be a member of the group");
+ assertTrue(Group.getCurrentGroup().get().isMember("Bob"), "Bob should be a member of the group");
+ }
+}
diff --git a/src/test/java/seedu/duke/HelpTest.java b/src/test/java/seedu/duke/HelpTest.java
new file mode 100644
index 0000000000..822b9b184c
--- /dev/null
+++ b/src/test/java/seedu/duke/HelpTest.java
@@ -0,0 +1,46 @@
+package seedu.duke;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+public class HelpTest {
+ private static final String prompt =
+ "Welcome, here is a list of commands:\n" +
+ "help: Access help menu.\n" +
+ "create : Create a group.\n" +
+ "exit : Exit current group.\n" +
+ "member : Add a member to the group.\n" +
+ "expense /amount " +
+ "/currency /paid " +
+ "/user /user ...: " +
+ "Add an expense SPLIT EQUALLY.\n" +
+ "expense /unequal /amount " +
+ "/currency /paid " +
+ "/user " +
+ "/user ...: " +
+ "Add an expense SPLIT UNEQUALLY.\n" +
+ "list: List all expenses in the group.\n" +
+ "balance : Show user's balance.\n" +
+ "settle /user : Settle the amount between two users.\n" +
+ "luck : luck is in the air tonight";
+
+ @Test
+ public void dummyTest() {
+ assertEquals(2, 2);
+ }
+ @Test
+ public void testPrint() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+ System.setOut(ps);
+ printHelp();
+ String output = baos.toString();
+ assertEquals(prompt, output);
+ }
+
+ static void printHelp() {
+ System.out.print(prompt);
+ }
+}
diff --git a/src/test/java/seedu/duke/LuckTest.java b/src/test/java/seedu/duke/LuckTest.java
new file mode 100644
index 0000000000..ba4d437eaf
--- /dev/null
+++ b/src/test/java/seedu/duke/LuckTest.java
@@ -0,0 +1,52 @@
+package seedu.duke;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+public class LuckTest {
+ private static final String testOutput =
+
+ " .=*+::. \n" +
+ " =*=-:::. \n" +
+ " .:=+**#***####*+=-:. \n" +
+ " :+#%%%%###%########%%%%*=: \n" +
+ " .=#%%%####################%%%#=. \n" +
+ " +#%%%####%%%###%%%###%%%%#*###%%#= \n" +
+ " :##%%########%%###%%###%%#########%%#: \n" +
+ " .##%#*#%%%%#######*##*#######%%%##*#%%#: \n" +
+ " *%%%##%%###%%%############%%%%%%%%##%%%#. \n" +
+ " .%#*****+*****++*++*****+**++***********%= \n" +
+ " :%*-::.-=-:::-==:==-:::-=:-===---==-.::+%+ \n" +
+ " :%*=:.+@@@%%@@@*=@@@#%%@@%-@@@@%@@@@-.-+#+ .=-.. \n" +
+ " :%*=.=%+*=+***@-%%+*=+##*@=*%++==**+%::+#=.##--- \n" +
+ " :%*=.#%-++=--*%-@#-++=--*@+=%-+*=--*@=:+#+ -::-: \n" +
+ " :%*=.#@%@%=-*@%-@@%@%=-#@@+=@%@%=-#@@=:+#+ -+: \n" +
+ " -%*=:=@@%---%@@:%@@%---%@@=*@@%---%@@::+#+ =*: \n" +
+ " -%*=:.#@@###@@@*+@@@###@@@-@@@@###@@=.-+#+ -+: \n" +
+ " -%*=:.:*##**+***:********+:***=+=**-..:+%*--+*: \n" +
+ " :%*+=====-==========-=-===============+*%*---- \n" +
+ " +##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%##*. \n" +
+ " =******************************************* \n" +
+ " =++=++++++++++++++++++++++++++++++++++++++++ \n" +
+ " =*++++++++++++++++++++++++++++++++++++++++*+ \n" +
+ " :----------------------------------------::. \n" +
+ "Gamble Gamble Gamble! Crazy Slots!!!\n" +
+ "10 USD Per round, given to random user in your group!!!\n" +
+ "Win if all 3 MIDDLE slots are the same and clear your debts!!!\n";
+
+ void testLuck() {
+ Group newGroup = new Group("lmao");
+ String username = "heehee";
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+ System.setOut(ps);
+ Luck newLuck = new Luck(newGroup, username);
+ newLuck.printWelcome();
+ String output = baos.toString();
+ assertEquals(testOutput, output);
+ }
+}
diff --git a/src/test/java/seedu/duke/MoneyTest.java b/src/test/java/seedu/duke/MoneyTest.java
new file mode 100644
index 0000000000..a6c4485f92
--- /dev/null
+++ b/src/test/java/seedu/duke/MoneyTest.java
@@ -0,0 +1,58 @@
+package seedu.duke;
+
+import org.junit.jupiter.api.Test;
+//@@author MonkeScripts
+public class MoneyTest {
+ @Test
+ void testMoney() {
+ Money a = new Money(10.00F, CurrencyConversions.MYR);
+ assert(a.getAmount() == 10.00);
+ assert(a.getCurrency().equals(CurrencyConversions.MYR)) ;
+ }
+
+ @Test
+ void testAddition() {
+ Money sg = new Money(10.00F, CurrencyConversions.SGD);
+ Money malaysia = new Money(10.00F, CurrencyConversions.MYR);
+ //total in SGD
+ Money total = sg.addition(malaysia, CurrencyConversions.SGD);
+ System.out.println(total.getAmount());
+ assert(total.getAmount() == 10.00F * CurrencyConversions.SGD.getRate()
+ + 10.00F * CurrencyConversions.MYR.getInverseRate());
+ assert(total.getCurrency().equals(CurrencyConversions.SGD));
+ assert(total.getAmount() == sg.convertToSGD().getAmount()
+ + malaysia.convertToSGD().getAmount());
+ assert(total.getCurrency().equals(CurrencyConversions.SGD));
+ //total in MYR
+ total = sg.addition(malaysia, CurrencyConversions.MYR);
+ assert(total.getAmount() == 10.00 * CurrencyConversions.MYR.getRate()
+ + 10.00);
+ assert(total.getAmount() ==
+ sg.convertToOther(CurrencyConversions.MYR).getAmount() + malaysia.getAmount());
+ assert(total.getCurrency().equals(CurrencyConversions.MYR));
+ }
+
+ @Test
+ void testMultiplication() {
+ Money jap = new Money(10000.00F, CurrencyConversions.JPY);
+ //multiplied by 3-fold and then converted to euro
+ Money multiplied = jap.multiplication(3, CurrencyConversions.EUR);
+ assert(multiplied.getAmount() == new Money(
+ 30000.00F, CurrencyConversions.JPY).
+ convertToOther(CurrencyConversions.EUR).getAmount());
+ assert(multiplied.getCurrency().equals(CurrencyConversions.EUR));
+ }
+
+ @Test
+ void testAdditionAndMultiplication() {
+ Money sg = new Money(10.00F, CurrencyConversions.SGD);
+ Money malaysia = new Money(10.00F, CurrencyConversions.MYR);
+ //compute total = sg + 3 * malaysia, converted to euro;
+ Money total = sg.addition(malaysia.multiplication(
+ 3, CurrencyConversions.MYR), CurrencyConversions.EUR);
+ assert(total.getAmount() ==
+ sg.addition(new Money(30.00F, CurrencyConversions.MYR),
+ CurrencyConversions.EUR).getAmount());
+ }
+
+}
diff --git a/src/test/java/seedu/duke/ParserTest.java b/src/test/java/seedu/duke/ParserTest.java
new file mode 100644
index 0000000000..2b8d53c027
--- /dev/null
+++ b/src/test/java/seedu/duke/ParserTest.java
@@ -0,0 +1,27 @@
+package seedu.duke;
+
+import org.junit.jupiter.api.Test;
+
+public class ParserTest {
+
+ public void testParser(String userInput, String command, String argument,
+ String[] amount, String[] paid, String[] user, String[] currency){
+ Parser parserFromInput = new Parser(userInput);
+ System.out.println(parserFromInput);
+
+ Parser parserFromParams = new Parser(userInput, command, argument, amount, paid, user, currency);
+ System.out.println(parserFromParams);
+
+ assert parserFromInput.toString().equals(parserFromParams.toString());
+ }
+
+ @Test
+ public void test1(){
+ testParser("command argument /amount amount /paid paid /currency SGD /user user1 /user user2",
+ "command", "argument",
+ new String[]{"amount"},
+ new String[]{"paid"},
+ new String[]{"user1", "user2"},
+ new String[]{"SGD"});
+ }
+}
diff --git a/src/test/java/seedu/duke/SettleTest.java b/src/test/java/seedu/duke/SettleTest.java
new file mode 100644
index 0000000000..f1f7c465c8
--- /dev/null
+++ b/src/test/java/seedu/duke/SettleTest.java
@@ -0,0 +1,67 @@
+//@@author avrilgk
+
+package seedu.duke;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import seedu.duke.exceptions.ExpensesException;
+
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SettleTest {
+
+ private Group group;
+
+ @BeforeEach
+ public void setup() throws ExpensesException {
+ group = new Group("Test Group");
+ User payer = new User("Alice");
+ User payee = new User("Bob");
+ group.addMember(payer.getName());
+ group.addMember(payee.getName());
+ ArrayList> payees = new ArrayList<>();
+ Money payeeAmount = new Money(50.0f,CurrencyConversions.SGD);
+ payees.add(new Pair<>(payee.getName(), payeeAmount));
+ Money totalAmount = new Money(100.0f,CurrencyConversions.SGD);
+ Expense expense = new Expense(payer.getName(), "Test Expense", totalAmount, payees);
+ group.addExpense(expense);
+ }
+
+ @Test
+ public void testSettleCreation() {
+ User payer = new User("Alice");
+ User payee = new User("Bob");
+ Settle settle = new Settle(payer, payee, 50.0F);
+ assertEquals("Alice", settle.getPayer());
+ assertEquals("Alice paid Bob 50.0", settle.toString());
+ }
+
+ @Test
+ public void testSettleCreationWithNegativeAmount() {
+ User payer = new User("Alice");
+ User payee = new User("Bob");
+ Settle settle = new Settle(payer, payee, -50.0F);
+ assertEquals("Alice", settle.getPayer());
+ assertEquals("Alice paid Bob -50.0", settle.toString());
+ }
+
+ @Test
+ public void testSettleCreationWithZeroAmount() {
+ User payer = new User("Alice");
+ User payee = new User("Bob");
+ Settle settle = new Settle(payer, payee, 0.0F);
+ assertEquals("Alice", settle.getPayer());
+ assertEquals("Alice paid Bob 0.0", settle.toString());
+ }
+
+ @Test
+ public void testSettleCreationWithZeroAmountAndNegativeAmount() {
+ User payer = new User("Alice");
+ User payee = new User("Bob");
+ Settle settle = new Settle(payer, payee, 0.0F);
+ assertEquals("Alice", settle.getPayer());
+ assertEquals("Alice paid Bob 0.0", settle.toString());
+ }
+}
diff --git a/src/test/java/seedu/duke/UserInterfaceTest.java b/src/test/java/seedu/duke/UserInterfaceTest.java
new file mode 100644
index 0000000000..fd9f6e4889
--- /dev/null
+++ b/src/test/java/seedu/duke/UserInterfaceTest.java
@@ -0,0 +1,13 @@
+package seedu.duke;
+
+import org.junit.jupiter.api.Test;
+
+public class UserInterfaceTest {
+ @Test
+ public void printTest() {
+ UserInterface.printMessage("Success", MessageType.SUCCESS);
+ UserInterface.printMessage("Message");
+ UserInterface.printMessage("Error", MessageType.ERROR);
+
+ }
+}
diff --git a/src/test/java/seedu/duke/storage/GroupStorageTest.java b/src/test/java/seedu/duke/storage/GroupStorageTest.java
new file mode 100644
index 0000000000..3a9bdd1171
--- /dev/null
+++ b/src/test/java/seedu/duke/storage/GroupStorageTest.java
@@ -0,0 +1,80 @@
+package seedu.duke.storage;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import seedu.duke.exceptions.GroupLoadException;
+import seedu.duke.exceptions.GroupSaveException;
+import seedu.duke.Group;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class GroupStorageTest {
+ private static final String TEST_GROUPS_DIRECTORY = "src/test/data/groups";
+
+ private GroupStorage groupStorage;
+
+ @BeforeAll
+ static void setUpTestDirectory() {
+ GroupFilePath.setGroupsDirectory(TEST_GROUPS_DIRECTORY);
+ }
+
+ @BeforeEach
+ void setUp() throws IOException {
+ FileIO fileIO = new FileIOImpl();
+ groupStorage = new GroupStorage(fileIO);
+
+ // Create the test groups directory if it doesn't exist
+ Files.createDirectories(Path.of(TEST_GROUPS_DIRECTORY));
+ }
+
+ @Test
+ void saveGroupToFile_validGroup_successfullySaves() throws GroupSaveException {
+ Group group = createSampleGroup();
+
+ groupStorage.saveGroupToFile(group);
+
+ Path filePath = Path.of(TEST_GROUPS_DIRECTORY, "sample_group.txt");
+ assertTrue(Files.exists(filePath));
+ }
+
+ @Test
+ void loadGroupFromFile_validFile_successfullyLoads() throws IOException, GroupLoadException {
+ createSampleGroupFile();
+
+ Group loadedGroup = groupStorage.loadGroupFromFile("sample_group");
+
+ assertNotNull(loadedGroup);
+ assertEquals("sample_group", loadedGroup.getGroupName());
+ assertEquals(2, loadedGroup.getMembers().size());
+ }
+
+ @Test
+ void loadGroupFromFile_nonExistentFile_throwsGroupLoadException() {
+ assertThrows(GroupLoadException.class, () -> groupStorage.loadGroupFromFile("nonexistent_group"));
+ }
+
+ private Group createSampleGroup() {
+ Group group = Group.getOrCreateGroup("sample_group").get();
+ group.addMember("user1");
+ group.addMember("user2");
+ return group;
+ }
+
+ private void createSampleGroupFile() throws IOException {
+ Path filePath = Path.of(TEST_GROUPS_DIRECTORY, "sample_group.txt");
+ String fileContent = "sample_group\n" +
+ "Members:\n" +
+ "user1\n" +
+ "user2\n" +
+ "Expenses:\n";
+ Files.writeString(filePath, fileContent);
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 892cb6cae7..001f2fa26a 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,9 +1,33 @@
Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
+.------..------..------..------..------..------..------..------..------..------.
+|S.--. ||P.--. ||L.--. ||I.--. ||T.--. ||L.--. ||I.--. ||A.--. ||N.--. ||G.--. |
+| :/\: || :/\: || :/\: || (\/) || :/\: || :/\: || (\/) || (\/) || :(): || :/\: |
+| :\/: || (__) || (__) || :\/: || (__) || (__) || :\/: || :\/: || ()() || :\/: |
+| '--'S|| '--'P|| '--'L|| '--'I|| '--'T|| '--'L|| '--'I|| '--'A|| '--'N|| '--'G|
+`------'`------'`------'`------'`------'`------'`------'`------'`------'`------'
-What is your name?
-Hello James Gosling
+Start splitting your expenses now!
+Welcome, here is a list of commands:
+help: Access help menu.
+create : Create a group.
+exit : Exit current group.
+member : Add a member to the group.
+expense /amount /currency /paid /user /user ...: Add an expense SPLIT EQUALLY.
+expense /unequal /amount /currency /paid /user /user ...: Add an expense SPLIT UNEQUALLY.
+list: List all expenses in the group.
+balance : Show user's balance.
+settle /user : Settle the amount between two users.
+luck : luck is in the air tonight
+That is not a command. Please use one of the commands given here
+Welcome, here is a list of commands:
+help: Access help menu.
+create : Create a group.
+exit : Exit current group.
+member : Add a member to the group.
+expense /amount /currency /paid /user /user ...: Add an expense SPLIT EQUALLY.
+expense /unequal /amount /currency /paid /user /user ...: Add an expense SPLIT UNEQUALLY.
+list: List all expenses in the group.
+balance : Show user's balance.
+settle /user : Settle the amount between two users.
+luck : luck is in the air tonight
+Goodbye!