diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java
index 3caea4b..10ae9b9 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/Main.java
@@ -3,8 +3,8 @@
import edu.ntnu.idi.idatt2003.g40.mappe.engine.Exchange;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Player;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Stock;
-import edu.ntnu.idi.idatt2003.g40.mappe.service.FileConverter;
-import edu.ntnu.idi.idatt2003.g40.mappe.service.FileParser;
+import edu.ntnu.idi.idatt2003.g40.mappe.service.StockFileParser;
+import edu.ntnu.idi.idatt2003.g40.mappe.service.StockFileManager;
import edu.ntnu.idi.idatt2003.g40.mappe.service.SaveGameService;
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventManager;
import edu.ntnu.idi.idatt2003.g40.mappe.utils.ConfigValues;
@@ -86,9 +86,9 @@ public void start(final Stage stage) throws Exception {
ViewManager viewManager = new ViewManager(stage, eventManager);
List stocksInFile;
- FileParser parser1 = new FileParser("/sp500.csv");
+ StockFileManager parser1 = new StockFileManager("src/main/resources/sp500.csv");
- FileConverter converter1 = new FileConverter();
+ StockFileParser converter1 = new StockFileParser();
stocksInFile = converter1.getStocksFromStrings(parser1.readFile());
Exchange exchange = new Exchange("Exchange", stocksInFile);
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/Exchange.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/Exchange.java
index b73f634..9cf5230 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/Exchange.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/Exchange.java
@@ -1,38 +1,35 @@
package edu.ntnu.idi.idatt2003.g40.mappe.engine;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Player;
-import edu.ntnu.idi.idatt2003.g40.mappe.model.Purchase;
-import edu.ntnu.idi.idatt2003.g40.mappe.model.Sale;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Share;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Stock;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Transaction;
-import edu.ntnu.idi.idatt2003.g40.mappe.service.*;
-import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventData;
-import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventManager;
-import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventPublisher;
-import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventType;
+import edu.ntnu.idi.idatt2003.g40.mappe.service.TransactionFactory;
+import edu.ntnu.idi.idatt2003.g40.mappe.service.TransactionType;
import edu.ntnu.idi.idatt2003.g40.mappe.utils.Validator;
-import javafx.beans.property.IntegerProperty;
-import javafx.beans.property.SimpleIntegerProperty;
-
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
+import javafx.beans.property.ReadOnlyIntegerProperty;
+import javafx.beans.property.ReadOnlyIntegerWrapper;
/**
* Represents a stock exchange where stocks can be traded.
*
- * Holds a map of stocks where stock symbol is key and stock is value.
+ * Holds a map of {@link Stock} objects where stock symbol is key and
+ * stock is value.
*
* Delegates buying and selling to player elements using calculators
*
* Advances week.
*
* @see Player
- * @see TransactionCalculator
+ * @see edu.ntnu.idi.idatt2003.g40.mappe.service.TransactionCalculator
+ *
+ * @version 1.1.0
* */
public final class Exchange {
@@ -44,7 +41,7 @@ public final class Exchange {
/**
* Current week (set to 1 in constructor).
* */
- private final IntegerProperty week = new SimpleIntegerProperty(1);
+ private final ReadOnlyIntegerWrapper week = new ReadOnlyIntegerWrapper(1);
/**
* Map of {@link Stock} objects. Key is stock symbol. Value is stock.
@@ -59,13 +56,16 @@ public final class Exchange {
/**
* Constructor.
*
- * @param name name of exchange.
- * @param stocks list of {@link Stock} objects.
+ * @param name name of exchange.
+ * @param stocks list of {@link Stock} objects.
*
* @throws IllegalArgumentException if name or stocks are empty/null.
* */
- public Exchange(final String name, final List stocks) throws IllegalArgumentException {
- if (!Validator.NOT_EMPTY.isValid(name) || stocks == null || stocks.isEmpty()) {
+ public Exchange(final String name, final List stocks)
+ throws IllegalArgumentException {
+ if (!Validator.NOT_EMPTY.isValid(name)
+ || stocks == null
+ || stocks.isEmpty()) {
throw new IllegalArgumentException("Invalid exchange parameters!");
}
this.name = name;
@@ -95,12 +95,12 @@ public int getWeek() {
}
/**
- * Getter method for the {@link IntegerProperty} object of week.
+ * Getter method for the {@link ReadOnlyIntegerProperty} object of week.
*
* @return week.
* */
- public IntegerProperty weekProperty() {
- return week;
+ public ReadOnlyIntegerProperty weekProperty() {
+ return week.getReadOnlyProperty();
}
/**
@@ -108,7 +108,7 @@ public IntegerProperty weekProperty() {
*
* @param symbol the stock symbol.
*
- * @return true or false.
+ * @return true or false.
* */
public boolean hasStock(final String symbol) {
return stockMap.containsKey(symbol);
@@ -117,16 +117,18 @@ public boolean hasStock(final String symbol) {
/**
* Getter method for stock element.
*
- * @param symbol the symbol of the stock to get.
+ * @param symbol the symbol of the stock to get.
*
- * @return {@link Stock} element gotten.
+ * @return {@link Stock} element gotten.
*
- * @throws IllegalArgumentException if symbol is invalid.
+ * @throws IllegalArgumentException if symbol is invalid or not in exchange.
* */
- public Stock getStock(final String symbol) throws IllegalArgumentException {
- if (!Validator.VALID_STOCK_NAME.isValid(symbol)) {
+ public Stock getStock(final String symbol)
+ throws IllegalArgumentException {
+ if (!Validator.VALID_STOCK_SYMBOL.isValid(symbol)
+ || !stockMap.containsKey(symbol)) {
throw new IllegalArgumentException(
- Validator.VALID_STOCK_NAME.getErrorMessage());
+ Validator.VALID_STOCK_SYMBOL.getErrorMessage());
}
return stockMap.get(symbol);
}
@@ -136,7 +138,7 @@ public Stock getStock(final String symbol) throws IllegalArgumentException {
*
* @param searchTerm the term to search for.
*
- * @return a list of {@link Stock} objects.
+ * @return a list of {@link Stock} objects.
* */
public List findStocks(final String searchTerm) {
List result = new ArrayList<>();
@@ -154,24 +156,27 @@ public List findStocks(final String searchTerm) {
/**
* Method called when a player buys a stock.
*
- * @param symbol the stock this player buys.
- * @param quantity the amount of stock to buy.
- * @param player the player buying stock.
+ * @param symbol the stock this player buys.
+ * @param quantity the amount of stock to buy.
+ * @param player the player buying stock.
*
- * @return Transaction representing the transaction.
+ * @return Transaction representing the transaction.
*
* @throws IllegalArgumentException if symbol or player is invalid.
* */
public Transaction buy(final String symbol,
final BigDecimal quantity,
final Player player) throws IllegalArgumentException {
- if (!Validator.VALID_STOCK_NAME.isValid(symbol) || player == null) {
+ if (!Validator.VALID_STOCK_SYMBOL.isValid(symbol)
+ || quantity == null
+ || player == null) {
throw new IllegalArgumentException("Invalid purchase!");
}
- Stock stock = stockMap.get(symbol);
+ Stock stock = getStock(symbol);
Share share = new Share(stock, quantity, stock.getSalesPrice());
- TransactionCalculator calculator = new PurchaseCalculator(share);
- Purchase purchase = new Purchase(share, week.get(), calculator);
+ Transaction purchase = TransactionFactory.createTransaction(
+ TransactionType.PURCHASE, share, getWeek()
+ );
player.handleTransaction(purchase);
return purchase;
}
@@ -179,10 +184,10 @@ public Transaction buy(final String symbol,
/**
* Method called when a player sells share.
*
- * @param share the share to sell.
- * @param player the player buying stock.
+ * @param share the share to sell.
+ * @param player the player buying stock.
*
- * @return Transaction representing the transaction.
+ * @return Transaction representing the transaction.
*
* @throws IllegalArgumentException if share or player is null.
* */
@@ -191,75 +196,84 @@ public Transaction sell(final Share share, final Player player)
if (share == null || player == null) {
throw new IllegalArgumentException("Invalid sell!");
}
- TransactionCalculator calculator = new SaleCalculator(share);
- Sale sale = new Sale(share, week.get(), calculator);
+ Transaction sale = TransactionFactory.createTransaction(
+ TransactionType.SALE, share, getWeek()
+ );
player.handleTransaction(sale);
return sale;
}
/**
- * Method called when a player sells share.
+ * Method called when a player sells share,
+ * defined by an amount instead of specific {@link Share} object.
+ *
+ * {@link edu.ntnu.idi.idatt2003.g40.mappe.model.Portfolio}
*
- * @param amount the amount of "shares" to sell.
+ * @param amount the amount of "shares" to sell.
* @param stockSymbol the stock to sell shares in.
- * @param player the player buying stock.
+ * @param player the player buying stock.
*
- * @return Transaction representing the transaction.
+ * @return List of transactions commited to sell the shares.
*
- * @throws IllegalArgumentException if any parameter is null, or if player does not have enough shares.
+ * @throws IllegalArgumentException if any parameter is null,
+ * or if player does not have enough shares,
+ * or if player does not own any shares
+ * of the given stock.
* */
- public List sell(BigDecimal amount,
+ public Transaction sell(BigDecimal amount,
final String stockSymbol,
final Player player)
throws IllegalArgumentException {
- if (amount == null || player == null || !Validator.NOT_EMPTY.isValid(stockSymbol)) {
- throw new IllegalArgumentException("Invalid sell!");
- } else {
+ if (amount == null
+ || player == null
+ || !Validator.VALID_STOCK_SYMBOL.isValid(stockSymbol)) {
+ throw new IllegalArgumentException("Invalid sell parameters!");
+ }
- List sharesOfStock = player.getPortfolio().getShares().stream()
- .filter(s -> s.getStock().getSymbol().equals(stockSymbol))
- .toList();
+ List matchingShares = player.getPortfolio().getShares(stockSymbol);
- BigDecimal totalOwned = player.getPortfolio().getTotalSharesBySymbol(stockSymbol);
+ if (matchingShares.isEmpty()) {
+ throw new IllegalArgumentException("Player does not own any shares of this stock!");
+ }
- if (amount.compareTo(totalOwned) > 0) {
- amount = totalOwned;
- }
- ArrayList transactions = new ArrayList<>();
- BigDecimal remainingToSell = amount;
+ Share ownedPosition = matchingShares.getFirst();
+ BigDecimal totalOwned = ownedPosition.getQuantity();
- for (Share share : sharesOfStock) {
- if (remainingToSell.compareTo(BigDecimal.ZERO) <= 0) {
- break;
- }
+ if (amount.compareTo(totalOwned) > 0) {
+ amount = totalOwned;
+ }
- BigDecimal shareQty = share.getQuantity();
+ Stock stock = ownedPosition.getStock();
+ Share shareToSell = new Share(stock, amount, stock.getSalesPrice());
- if (shareQty.compareTo(remainingToSell) <= 0) {
- remainingToSell = remainingToSell.subtract(shareQty);
- transactions.add(sell(share, player));
- } else {
- Share newShare = player.getPortfolio().splitShare(share, remainingToSell);
- remainingToSell = BigDecimal.ZERO;
- transactions.add(sell(newShare, player));
- }
- }
- return transactions;
- }
+ Transaction sale = TransactionFactory.createTransaction(
+ TransactionType.SALE, shareToSell, getWeek()
+ );
+ player.handleTransaction(sale);
+
+ return sale;
}
/**
* Method for advancing time, increasing the amount of weeks.
+ *
+ * Applies a random price change from -5% to 5% to every stock,
+ * plus a flat percent determined by their fortune, that can range from
+ * -10% to +10%.
+ *
+ * @see edu.ntnu.idi.idatt2003.g40.mappe.view.widgets.minigames.GameEngineView
* */
public void advance() {
for (Stock stock : stockMap.values()) {
BigDecimal currentPrice = stock.getSalesPrice();
- double change = ((random.nextDouble() * 0.10) - 0.05) + stock.getFortune();
+ BigDecimal change = BigDecimal.valueOf(random.nextDouble() * 0.10 - 0.05)
+ .add(BigDecimal.valueOf(stock.getFortune()));
stock.setFortune(0);
- BigDecimal factor = BigDecimal.valueOf(1 + change);
+ BigDecimal factor = BigDecimal.ONE.add(change);
- BigDecimal newPrice = currentPrice.multiply(factor);
+ BigDecimal newPrice = currentPrice.multiply(factor)
+ .setScale(2, java.math.RoundingMode.HALF_UP);
stock.addNewSalesPrice(newPrice);
}
week.set(week.get() + 1);
@@ -269,11 +283,17 @@ public void advance() {
* Method for getting the stocks with the most
* amount of increase since last week.
*
- * @param limit the maximum amount of stocks returned
+ * @param limit the maximum amount of stocks returned
*
* @return list of {@link Stock} objects.
+ *
+ * @throws IllegalArgumentException if limit is invalid (negative or zero).
* */
- public List getGainers(final int limit) {
+ public List getGainers(final int limit)
+ throws IllegalArgumentException {
+ if (limit < 1) {
+ throw new IllegalArgumentException("Invalid limit for getting gainers!");
+ }
return stockMap.entrySet().stream()
// We only want the stocks with a positive price change.
.filter(e ->
@@ -294,11 +314,17 @@ public List getGainers(final int limit) {
* Method for getting the stocks with the highest
* loss of price since last week.
*
- * @param limit the maximum amount of stocks returned
+ * @param limit the maximum amount of stocks returned
*
* @return list of {@link Stock} objects.
+ *
+ * @throws IllegalArgumentException if limit is invalid (negative or zero).
* */
- public List getLosers(final int limit) {
+ public List getLosers(final int limit)
+ throws IllegalArgumentException {
+ if (limit < 1) {
+ throw new IllegalArgumentException("Invalid limit for getting losers!");
+ }
return stockMap.entrySet().stream()
// Only get entries with negative price change.
.filter(e ->
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/TransactionArchive.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/TransactionArchive.java
index dadc496..b7b9cf5 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/TransactionArchive.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/TransactionArchive.java
@@ -3,11 +3,15 @@
import edu.ntnu.idi.idatt2003.g40.mappe.model.Purchase;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Sale;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Transaction;
+import edu.ntnu.idi.idatt2003.g40.mappe.utils.Validator;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
- * Stores completed transactions.
+ * Stores completed transactions in an {@link ArrayList} object.
+ *
+ * @version 1.1.0
*/
public final class TransactionArchive {
@@ -20,6 +24,7 @@ public final class TransactionArchive {
* Creates an empty transaction archive.
*/
public TransactionArchive() {
+ // Empty constructor.
}
/**
@@ -28,8 +33,13 @@ public TransactionArchive() {
* @param transaction the transaction to add
*
* @return true if the transaction was added
+ *
+ * @throws IllegalArgumentException if transaction is null
*/
public boolean add(final Transaction transaction) {
+ if (transaction == null) {
+ throw new IllegalArgumentException("Transaction cannot be null!");
+ }
return transactions.add(transaction);
}
@@ -48,24 +58,27 @@ public boolean isEmpty() {
* @param week the week number
*
* @return list of transactions from the given week
+ *
+ * @throws IllegalArgumentException if week is less than 1.
*/
public List getTransactions(final int week) {
- List result = new ArrayList<>();
- for (Transaction transaction : transactions) {
- if (transaction.getWeek() == week) {
- result.add(transaction);
- }
+ if (!Validator.VALID_WEEK.isValid(Integer.toString(week))) {
+ throw new IllegalArgumentException(
+ Validator.VALID_WEEK.getErrorMessage()
+ );
}
- return result;
+ return transactions.stream()
+ .filter(transaction -> transaction.getWeek() == week)
+ .toList();
}
/**
- * Returns all transactions.
+ * Returns an un-mutable reference to the transactions list.
*
- * @return list of transactions from the given week
+ * @return unmodifiable version of list.
*/
public List getTransactions() {
- return transactions;
+ return Collections.unmodifiableList(transactions);
}
/**
@@ -74,16 +87,15 @@ public List getTransactions() {
* @param week the week number
*
* @return list of purchases from the given week
+ *
+ * @throws IllegalArgumentException if week is less than 1.
*/
- public List getPurchases(final int week) {
- List result = new ArrayList<>();
- for (Transaction transaction : transactions) {
- if (transaction instanceof Purchase purchase
- && transaction.getWeek() == week) {
- result.add(purchase);
- }
- }
- return result;
+ public List getPurchases(final int week)
+ throws IllegalArgumentException {
+ return getTransactions(week).stream()
+ .filter(Purchase.class::isInstance)
+ .map(Purchase.class::cast)
+ .toList();
}
/**
@@ -92,15 +104,15 @@ public List getPurchases(final int week) {
* @param week the week number
*
* @return list of sales from the given week
+ *
+ * @throws IllegalArgumentException if week is less than 1.
*/
- public List getSales(final int week) {
- List result = new ArrayList<>();
- for (Transaction transaction : transactions) {
- if (transaction instanceof Sale sale && transaction.getWeek() == week) {
- result.add(sale);
- }
- }
- return result;
+ public List getSales(final int week)
+ throws IllegalArgumentException {
+ return getTransactions(week).stream()
+ .filter(Sale.class::isInstance)
+ .map(Sale.class::cast)
+ .toList();
}
/**
@@ -109,13 +121,9 @@ public List getSales(final int week) {
* @return number of distinct weeks
*/
public int countDistinctWeeks() {
- List weeks = new ArrayList<>();
- for (Transaction transaction : transactions) {
- int week = transaction.getWeek();
- if (!weeks.contains(week)) {
- weeks.add(week);
- }
- }
- return weeks.size();
+ return (int) transactions.stream()
+ .map(Transaction::getWeek)
+ .distinct()
+ .count();
}
}
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/exceptions/NotEnoughMoneyException.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/exceptions/NotEnoughMoneyException.java
new file mode 100644
index 0000000..8d19749
--- /dev/null
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/exceptions/NotEnoughMoneyException.java
@@ -0,0 +1,18 @@
+package edu.ntnu.idi.idatt2003.g40.mappe.exceptions;
+
+/**
+ * Exception primarily thrown when the active
+ * {@link edu.ntnu.idi.idatt2003.g40.mappe.model.Player} object
+ * does not have enough money for a transaction to complete.
+ * */
+public class NotEnoughMoneyException extends RuntimeException {
+
+ /**
+ * Constructor.
+ *
+ * @param message the exception message.
+ * */
+ public NotEnoughMoneyException(final String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Player.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Player.java
index 9cac71e..0b66b11 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Player.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Player.java
@@ -2,10 +2,11 @@
import edu.ntnu.idi.idatt2003.g40.mappe.controller.PlayerStatusController;
import edu.ntnu.idi.idatt2003.g40.mappe.engine.TransactionArchive;
+import edu.ntnu.idi.idatt2003.g40.mappe.exceptions.NotEnoughMoneyException;
import edu.ntnu.idi.idatt2003.g40.mappe.utils.Validator;
import java.math.BigDecimal;
-import javafx.beans.property.FloatProperty;
-import javafx.beans.property.SimpleFloatProperty;
+import javafx.beans.property.ReadOnlyFloatProperty;
+import javafx.beans.property.ReadOnlyFloatWrapper;
/**
* Represents a player in the system.
@@ -18,6 +19,8 @@
* Has a set amount of money to use on said exchange.
* Has a {@link TransactionArchive}
*
+ *
+ * @version 1.1.0
* */
public final class Player {
@@ -37,14 +40,18 @@ public final class Player {
private BigDecimal money;
/**
- * Current net-worth of player as a listenable {@link FloatProperty} object.
+ * Current net-worth of player as a listenable,
+ * read-only, {@link ReadOnlyFloatWrapper} object.
* */
- private final FloatProperty networthAsFloatProp = new SimpleFloatProperty(0);
+ private final ReadOnlyFloatWrapper networthAsFloatProp =
+ new ReadOnlyFloatWrapper(0f);
/**
- * Current money of player as a listenable {@link FloatProperty} object.
+ * Current money of player as a read-only
+ * {@link ReadOnlyFloatWrapper} object.
* */
- private final FloatProperty moneyAsFloatProp = new SimpleFloatProperty(0);
+ private final ReadOnlyFloatWrapper moneyAsFloatProp
+ = new ReadOnlyFloatWrapper(0f);
/**
* The players' portfolio, holding their shares.
@@ -63,19 +70,28 @@ public final class Player {
* @param name the name of the player
* @param startingMoney the starting amount of money
*
- * @throws IllegalArgumentException if name is null.
+ * @throws IllegalArgumentException if name is empty,
+ * or starting money is null,
+ * zero or negative.
*/
- public Player(final String name, final BigDecimal startingMoney) throws IllegalArgumentException {
+ public Player(final String name,
+ final BigDecimal startingMoney)
+ throws IllegalArgumentException {
if (!Validator.NOT_EMPTY.isValid(name)) {
- throw new IllegalArgumentException("Invalid name!");
+ throw new IllegalArgumentException("Player name cannot be empty!");
+ }
+ if (startingMoney == null
+ || startingMoney.compareTo(BigDecimal.ZERO) <= 0) {
+ throw new IllegalArgumentException(
+ "Starting money cannot be null, zero, or negative!"
+ );
}
this.name = name;
this.startingMoney = startingMoney;
this.money = this.startingMoney;
- this.networthAsFloatProp.setValue(this.startingMoney);
- this.moneyAsFloatProp.setValue(this.startingMoney);
this.portfolio = new Portfolio();
this.transactionArchive = new TransactionArchive();
+ updateObservableProperties();
}
/**
@@ -109,18 +125,42 @@ public BigDecimal getMoney() {
* Adds money to the players balance.
*
* @param amount the amount to add
+ *
+ * @throws IllegalArgumentException if money to add is negative or zero.
*/
- public void addMoney(final BigDecimal amount) {
+ public void addMoney(final BigDecimal amount)
+ throws IllegalArgumentException {
+ if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
+ throw new IllegalArgumentException(
+ "Can only add positive values to player!"
+ );
+ }
money = money.add(amount);
+ updateObservableProperties();
}
/**
* Withdraws money from the players balance.
*
* @param amount the amount to withdraw
+ *
+ * @throws IllegalArgumentException if money to withdraw is negative or zero,
+ * or if amount is more than current money.
*/
- public void withdrawMoney(final BigDecimal amount) {
+ public void withdrawMoney(final BigDecimal amount)
+ throws IllegalArgumentException {
+ if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
+ throw new IllegalArgumentException(
+ "Amount to withdraw must be positive!"
+ );
+ }
+ if (money.compareTo(amount) < 0) {
+ throw new IllegalArgumentException(
+ "Cannot withdraw more money than available balance!"
+ );
+ }
money = money.subtract(amount);
+ updateObservableProperties();
}
/**
@@ -152,21 +192,23 @@ public BigDecimal getNetWorth() {
}
/**
- * Get net-worth as a {@link FloatProperty} object, allowing listening for changes.
+ * Get net-worth as a {@link ReadOnlyFloatProperty} object,
+ * allowing listening for changes.
*
- * @return FloatProperty.
+ * @return networth as an immutable value.
* */
- public FloatProperty getNetWorthAsFloatProperty() {
- return networthAsFloatProp;
+ public ReadOnlyFloatProperty getNetWorthAsFloatProperty() {
+ return networthAsFloatProp.getReadOnlyProperty();
}
/**
- * Get money as a {@link FloatProperty} object, allowing listening for changes.
+ * Get money as a {@link ReadOnlyFloatProperty} object,
+ * allowing listening for changes.
*
- * @return FloatProperty.
+ * @return money as an immutable value.
* */
- public FloatProperty getMoneyAsFloatProperty() {
- return moneyAsFloatProp;
+ public ReadOnlyFloatProperty getMoneyAsFloatProperty() {
+ return moneyAsFloatProp.getReadOnlyProperty();
}
/**
@@ -184,22 +226,46 @@ public PlayerStatus getStatus() {
* Method for handling a transaction for the player.
*
* @param transaction the transaction to handle.
+ *
+ * @throws IllegalArgumentException if transaction is null.
+ * @throws NotEnoughMoneyException if player does not have enough
+ * money for the transaction.
* */
- public void handleTransaction(final Transaction transaction) {
+ public void handleTransaction(final Transaction transaction)
+ throws IllegalArgumentException, NotEnoughMoneyException {
+ if (transaction == null) {
+ throw new IllegalArgumentException("Cannot handle null transaction!");
+ }
+
if (transaction instanceof Purchase purchase) {
- if (money.floatValue() > transaction.getCalculator().calculateTotal().floatValue()) {
+ BigDecimal totalCost = purchase.getCalculator().calculateTotal();
+ if (this.money.compareTo(totalCost) < 0) {
+ throw new NotEnoughMoneyException("Not enough money for transaction!");
+ }
+ }
+
+ switch (transaction) {
+ case Purchase purchase -> {
withdrawMoney(purchase.getCalculator().calculateTotal());
portfolio.addShare(purchase.getShare());
- transactionArchive.add(transaction);
- transaction.commit(this);
}
- } else if (transaction instanceof Sale sale) {
- addMoney(sale.getCalculator().calculateTotal());
- portfolio.removeShare(sale.getShare());
- transactionArchive.add(transaction);
- transaction.commit(this);
+ case Sale sale -> {
+ addMoney(sale.getCalculator().calculateTotal());
+ portfolio.removeShare(sale.getShare());
+ }
+ default -> throw new IllegalStateException("Unexpected value: " + transaction);
}
- networthAsFloatProp.setValue(getNetWorth().floatValue());
- moneyAsFloatProp.setValue(money);
+ transactionArchive.add(transaction);
+ transaction.commit(this);
+
+ updateObservableProperties();
+ }
+
+ /**
+ * Helper method to synchronize the listener values.
+ */
+ private void updateObservableProperties() {
+ this.moneyAsFloatProp.setValue(this.money.floatValue());
+ this.networthAsFloatProp.setValue(this.getNetWorth().floatValue());
}
}
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Portfolio.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Portfolio.java
index 483adb8..415a065 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Portfolio.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Portfolio.java
@@ -1,26 +1,25 @@
package edu.ntnu.idi.idatt2003.g40.mappe.model;
-import edu.ntnu.idi.idatt2003.g40.mappe.service.PurchaseCalculator;
-import edu.ntnu.idi.idatt2003.g40.mappe.service.SaleCalculator;
import edu.ntnu.idi.idatt2003.g40.mappe.utils.Validator;
-
import java.math.BigDecimal;
-import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
-import java.util.Objects;
+import java.util.Map;
/**
* Represents a player's portfolio of shares.
*
* The portfolio stores shares and provides operations for adding, removing,
* retrieving and checking ownership of shares.
+ *
+ * @version 1.1.0
*/
public final class Portfolio {
/**
- * List of shares.
+ * Map used to handle internal shares.
* */
- private final List shares = new ArrayList<>();
+ private final Map shares = new HashMap<>();
/**
* Creates an empty portfolio.
@@ -30,24 +29,41 @@ public Portfolio() {
}
/**
- * Adds a share to the portfolio.
+ * Adds a share to the portfolio. If share already exists, merges shares.
*
* @param share the share to add
*
- * @return {@code true} if the share was added, {@code false} otherwise
- *
* @throws IllegalArgumentException if share is null.
*/
- public boolean addShare(final Share share) throws IllegalArgumentException {
+ public void addShare(final Share share) throws IllegalArgumentException {
if (share == null) {
throw new IllegalArgumentException("Invalid share!");
}
- return shares.add(share);
+ String symbol = share.getStock().getSymbol().toUpperCase();
+
+ if (shares.containsKey(symbol)) {
+ Share existingShare = shares.get(symbol);
+ BigDecimal totalQuantity =
+ existingShare.getQuantity().add(share.getQuantity());
+
+ shares.put(symbol,
+ new Share(
+ share.getStock(),
+ totalQuantity, existingShare.getPurchasePrice()
+ )
+ );
+ } else {
+ shares.put(symbol, share);
+ }
}
/**
* Removes a share from the portfolio.
*
+ * Uses the quantity value to deduct share amount from the map.
+ * If quantity to remove is equal to amount held, removes share entirely.
+ * If not, splits the share.
+ *
* @param share the share to remove
*
* @return {@code true} if the share was removed,
@@ -56,11 +72,37 @@ public boolean addShare(final Share share) throws IllegalArgumentException {
* @throws IllegalArgumentException if share is null.
*
*/
- public boolean removeShare(final Share share) throws IllegalArgumentException {
+ public boolean removeShare(final Share share)
+ throws IllegalArgumentException {
if (share == null) {
throw new IllegalArgumentException("Invalid share!");
}
- return shares.remove(share);
+ String symbol = share.getStock().getSymbol().toUpperCase();
+ if (!shares.containsKey(symbol)) {
+ return false;
+ }
+
+ Share ownedShare = shares.get(symbol);
+ int comparison = ownedShare.getQuantity().compareTo(share.getQuantity());
+
+ if (comparison < 0) {
+ throw new IllegalArgumentException(
+ "Cannot remove more shares than are currently owned!");
+ } else if (comparison == 0) {
+
+ shares.remove(symbol);
+ } else {
+ BigDecimal remainingQuantity =
+ ownedShare.getQuantity().subtract(share.getQuantity());
+ shares.put(symbol,
+ new Share(
+ share.getStock(),
+ remainingQuantity,
+ ownedShare.getPurchasePrice()
+ )
+ );
+ }
+ return true;
}
/**
@@ -69,7 +111,7 @@ public boolean removeShare(final Share share) throws IllegalArgumentException {
* @return a list of shares
*/
public List getShares() {
- return List.copyOf(shares);
+ return List.copyOf(shares.values());
}
/**
@@ -81,14 +123,14 @@ public List getShares() {
*
* @throws IllegalArgumentException if symbol is invalid.
*/
- public List getShares(final String symbol) throws IllegalArgumentException {
- if (!Validator.VALID_STOCK_NAME.isValid(symbol)) {
+ public List getShares(final String symbol)
+ throws IllegalArgumentException {
+ if (!Validator.VALID_STOCK_SYMBOL.isValid(symbol)) {
throw new IllegalArgumentException(
- Validator.VALID_STOCK_NAME.getErrorMessage());
+ Validator.VALID_STOCK_SYMBOL.getErrorMessage());
}
- return shares.stream()
- .filter(s -> symbol.equalsIgnoreCase(s.getStock().getSymbol()))
- .toList();
+ Share share = shares.get(symbol.toUpperCase());
+ return share != null ? List.of(share) : List.of();
}
/**
@@ -101,11 +143,15 @@ public List getShares(final String symbol) throws IllegalArgumentExceptio
*
* @throws IllegalArgumentException if share is null.
*/
- public boolean contains(final Share share) throws IllegalArgumentException {
+ public boolean contains(final Share share)
+ throws IllegalArgumentException {
if (share == null) {
throw new IllegalArgumentException("Invalid share!");
}
- return shares.contains(share);
+ String symbol = share.getStock().getSymbol().toUpperCase();
+ Share owned = shares.get(symbol);
+ return owned != null
+ && owned.getQuantity().compareTo(share.getQuantity()) >= 0;
}
/**
@@ -115,11 +161,11 @@ public boolean contains(final Share share) throws IllegalArgumentException {
* @return the net worth.
* */
public BigDecimal getNetWorth() {
- BigDecimal netWorth = new BigDecimal("0");
-
- for (Share s : shares) {
- SaleCalculator calculator = new SaleCalculator(s);
- netWorth = netWorth.add(calculator.calculateTotal());
+ BigDecimal netWorth = BigDecimal.ZERO;
+ for (Share s : shares.values()) {
+ netWorth = netWorth.add(
+ s.getQuantity().multiply(s.getStock().getSalesPrice())
+ );
}
return netWorth;
}
@@ -127,37 +173,16 @@ public BigDecimal getNetWorth() {
/**
* Helper method to get total amount of shares owned in a specific stock.
*
- * @param symbol the symbol of the stock to check for shares.
- * */
- public BigDecimal getTotalSharesBySymbol(final String symbol) {
- return shares.stream()
- .filter(s -> s.getStock().getSymbol().equals(symbol))
- .map(Share::getQuantity)
- .reduce(BigDecimal.ZERO, BigDecimal::add);
- }
-
- /**
- * "Splits" a share in two pieces based on an amount.
- *
- * @param share the share to split.
- * @param splitAmount the amount to split by.
- *
- * @return the split share from the original to the split amount.
+ * @param symbol the symbol of the stock to check for shares.
*
- * @throws IllegalArgumentException if share or split amount is invalid.
+ * @return BigDecimal representing total quantity of all
+ * shares of this symbol.
* */
- public Share splitShare(final Share share, final BigDecimal splitAmount)
- throws IllegalArgumentException {
- if (!contains(share) || splitAmount.compareTo(share.getQuantity()) > 0) {
- throw new IllegalArgumentException("Cannot split share!");
+ public BigDecimal getTotalShareQuantityBySymbol(final String symbol) {
+ if (symbol == null) {
+ return BigDecimal.ZERO;
}
- BigDecimal remainingAmount = share.getQuantity().subtract(splitAmount);
-
- Share newShare1 = new Share(share.getStock(), splitAmount, share.getPurchasePrice());
- Share newShare2 = new Share(share.getStock(), remainingAmount, share.getPurchasePrice());
- removeShare(share);
- addShare(newShare1);
- addShare(newShare2);
- return newShare1;
+ Share share = shares.get(symbol.toUpperCase());
+ return share != null ? share.getQuantity() : BigDecimal.ZERO;
}
}
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/SaveGame.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/SaveGame.java
index ff00e77..e6c60ec 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/SaveGame.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/SaveGame.java
@@ -1,5 +1,7 @@
package edu.ntnu.idi.idatt2003.g40.mappe.model;
+import edu.ntnu.idi.idatt2003.g40.mappe.utils.Validator;
+
/**
* Represents one save game entry.
*
@@ -14,7 +16,7 @@
* expected to be loaded with the default bundled stock data file.
*
*/
-public class SaveGame {
+public final class SaveGame {
/** Display name of the save. */
private final String name;
@@ -44,6 +46,11 @@ public SaveGame(final String name,
final double balance,
final double startingCapital,
final String stockDataPath) {
+ if (!Validator.NOT_EMPTY.isValid(name)
+ || balance <= 0
+ || startingCapital <= 0) {
+ throw new IllegalArgumentException("Invalid Save configuration!");
+ }
this.name = name;
this.balance = balance;
this.startingCapital = startingCapital;
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Share.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Share.java
index 3328a74..30aba43 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Share.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Share.java
@@ -32,13 +32,25 @@ public final class Share {
* @param quantity the quantity purchased
* @param purchasePrice the price per unit at purchase time
*
- * @throws IllegalArgumentException if stock is null.
+ * @throws IllegalArgumentException if parameters are null or invalid.
*/
public Share(final Stock stock,
final BigDecimal quantity,
final BigDecimal purchasePrice) throws IllegalArgumentException {
- if (stock == null) {
- throw new IllegalArgumentException("Invalid stock!");
+ if (stock == null
+ || quantity == null
+ || purchasePrice == null) {
+ throw new IllegalArgumentException("Invalid share configuration!");
+ }
+ if (quantity.compareTo(BigDecimal.ZERO) <= 0) {
+ throw new IllegalArgumentException(
+ "Quantity cannot be negative or zero!"
+ );
+ }
+ if (purchasePrice.compareTo(BigDecimal.ZERO) <= 0) {
+ throw new IllegalArgumentException(
+ "Purchase price cannot be negative or zero!"
+ );
}
this.stock = stock;
this.quantity = quantity;
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Stock.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Stock.java
index 1ca9664..d7ff05a 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Stock.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Stock.java
@@ -40,19 +40,26 @@ public final class Stock {
* @param symbol the unique stock symbol
* @param company the name of the company
* @param salesPrice the initial sales price of the stock
+ *
+ * @throws IllegalArgumentException if parameters are null or invalid.
*/
public Stock(final String symbol,
final String company,
final BigDecimal salesPrice) throws IllegalArgumentException {
- if (!Validator.VALID_STOCK_NAME.isValid(symbol)) {
+ if (!Validator.VALID_STOCK_SYMBOL.isValid(symbol)) {
throw new IllegalArgumentException(
- Validator.VALID_STOCK_NAME.getErrorMessage());
- } else {
- this.symbol = symbol;
- this.company = company;
- this.fortune = 0;
- prices.add(salesPrice);
+ Validator.VALID_STOCK_SYMBOL.getErrorMessage());
+ }
+ if (!Validator.NOT_EMPTY.isValid(company)) {
+ throw new IllegalArgumentException(Validator.NOT_EMPTY.getErrorMessage());
+ }
+ if (salesPrice == null || salesPrice.compareTo(BigDecimal.ZERO) <= 0) {
+ throw new IllegalArgumentException("Sales price of cannot be negative or zero!");
}
+ this.symbol = symbol;
+ this.company = company;
+ this.fortune = 0;
+ prices.add(salesPrice);
}
/**
@@ -109,11 +116,11 @@ public BigDecimal getSalesPrice() {
*/
public void addNewSalesPrice(final BigDecimal price)
throws IllegalArgumentException {
- if (price != null && price.intValue() != 0) {
- prices.add(price);
- } else {
- throw new IllegalArgumentException("Invalid price to add to stock: " + getSymbol());
+ if (price == null || price.compareTo(BigDecimal.ZERO) <= 0) {
+ throw new IllegalArgumentException("Invalid price to add to stock: "
+ + getSymbol());
}
+ prices.add(price);
}
/**
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Transaction.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Transaction.java
index fe7e6c5..62993f1 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Transaction.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Transaction.java
@@ -1,6 +1,7 @@
package edu.ntnu.idi.idatt2003.g40.mappe.model;
import edu.ntnu.idi.idatt2003.g40.mappe.service.TransactionCalculator;
+import edu.ntnu.idi.idatt2003.g40.mappe.utils.Validator;
/**
* Transaction abstract class.
@@ -40,14 +41,22 @@ public abstract class Transaction {
protected Transaction(final Share share, final int week,
final TransactionCalculator calculator)
- throws IllegalArgumentException{
- if (share == null || calculator == null) {
- throw new IllegalArgumentException("Invalid stock or calculator!");
- } else {
- this.share = share;
- this.week = week;
- this.calculator = calculator;
+ throws IllegalArgumentException {
+ if (share == null
+ || calculator == null) {
+ throw new IllegalArgumentException(
+ "Invalid configuration for transaction!"
+ );
}
+ if (!Validator.VALID_WEEK.isValid(Integer.toString(week))) {
+ throw new IllegalArgumentException(
+ Validator.VALID_WEEK.getErrorMessage()
+ );
+ }
+ this.share = share;
+ this.week = week;
+ this.calculator = calculator;
+
}
/**
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/SaveGameService.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/SaveGameService.java
index 2c6aaed..0350071 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/SaveGameService.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/SaveGameService.java
@@ -1,7 +1,6 @@
package edu.ntnu.idi.idatt2003.g40.mappe.service;
import edu.ntnu.idi.idatt2003.g40.mappe.model.SaveGame;
-
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/FileParser.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileManager.java
similarity index 67%
rename from src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/FileParser.java
rename to src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileManager.java
index 481d423..ac5cb7c 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/FileParser.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileManager.java
@@ -1,6 +1,10 @@
package edu.ntnu.idi.idatt2003.g40.mappe.service;
-import java.io.*;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -22,13 +26,13 @@
* to file, each stock separated by a line.
*
*
- * Used with {@link FileConverter}
+ * Used with {@link StockFileParser}
*
- * @see FileConverter
+ * @see StockFileParser
* @author tohja
* @version 1.0.0
* */
-public class FileParser {
+public class StockFileManager {
/** The path name this parser is using.*/
private final String pathName;
@@ -72,7 +76,7 @@ private enum ParserRuleSet {
try {
new BigDecimal(s);
return true;
- } catch (NumberFormatException e) {
+ } catch (NumberFormatException _) {
return false;
}
}),
@@ -102,13 +106,16 @@ private enum ParserRuleSet {
*
* @param pathName the file path name to read.
* */
- public FileParser(final String pathName) {
+ public StockFileManager(final String pathName) {
this.pathName = pathName;
}
/**
* Reads the file and returns a list element of all valid stocks as strings.
*
+ * If file is not found,
+ * falls back to default file in resources folder.
+ *
* Uses {@link BufferedReader} for opening a file stream.
*
* @return {@link List} object of all valid stock strings in file.
@@ -117,17 +124,20 @@ public FileParser(final String pathName) {
*
* @see Path
* */
-
public List readFile() throws IOException {
- try (InputStream inputStream = getClass().getResourceAsStream(pathName);
- BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+ Path path = Paths.get(pathName);
+
+ if (!Files.exists(path)) {
+ extractResourceFallback(path);
+ }
- List allLines = bufferedReader.readAllLines();
- List readableLines =
- allLines.stream()
- .filter(ParserRuleSet.VALID_FORMAT.rule).toList();
+ try (BufferedReader bufferedReader =
+ Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
+ List allLines = bufferedReader.lines().toList();
+ List readableLines = allLines.stream()
+ .filter(ParserRuleSet.VALID_FORMAT.rule)
+ .toList();
- // Valid lines (following the correct regular expressions)
return readableLines.stream().filter(s -> {
String[] parts = s.trim().split(",");
@@ -135,20 +145,45 @@ public List readFile() throws IOException {
return false;
}
- boolean validCode = ParserRuleSet
- .VALID_CODE.rule.test(parts[0].trim());
-
- boolean validName = ParserRuleSet
- .VALID_NAME.rule.test(parts[1].trim());
-
- boolean validPrice = ParserRuleSet
- .VALID_PRICE.rule.test(parts[2].trim());
+ boolean validCode =
+ ParserRuleSet.VALID_CODE.rule.test(parts[0].trim());
+ boolean validName =
+ ParserRuleSet.VALID_NAME.rule.test(parts[1].trim());
+ boolean validPrice =
+ ParserRuleSet.VALID_PRICE.rule.test(parts[2].trim());
return validCode && validName && validPrice;
}).toList();
} catch (IOException e) {
- throw new IOException("File parser could not parse file!");
+ throw new IOException("File parser could not parse file!", e);
+ }
+ }
+
+ /**
+ * Extracts the fallback template file from the application resources to
+ * the local file system.
+ *
+ * @param targetPath path to send the fallback file.
+ *
+ * @throws IOException if resource or input stream is not found or null.
+ */
+ private void extractResourceFallback(final Path targetPath)
+ throws IOException {
+ String resourceName = "/sp500.csv";
+
+ try (InputStream inputStream = getClass().getResourceAsStream(resourceName)) {
+ if (inputStream == null) {
+ throw new FileNotFoundException(
+ "Resource file not found in JAR: " + resourceName
+ );
+ }
+
+ if (targetPath.getParent() != null) {
+ Files.createDirectories(targetPath.getParent());
+ }
+
+ Files.copy(inputStream, targetPath);
}
}
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/FileConverter.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileParser.java
similarity index 58%
rename from src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/FileConverter.java
rename to src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileParser.java
index 397328d..07fd802 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/FileConverter.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileParser.java
@@ -3,7 +3,9 @@
import edu.ntnu.idi.idatt2003.g40.mappe.model.Stock;
import java.math.BigDecimal;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Converts stock objects to/from string format for file handling.
@@ -17,13 +19,13 @@
* list of string elements.
*
*
- * Used with {@link FileParser}
+ * Used with {@link StockFileManager}
*
- * @see FileParser
+ * @see StockFileManager
* @author tohja
* @version 1.0.0
* */
-public class FileConverter {
+public class StockFileParser {
/**
* Turns a list of valid string representations
@@ -42,29 +44,29 @@ public List getStocksFromStrings(final List validStocks)
throws IllegalArgumentException {
if (validStocks == null || validStocks.isEmpty()) {
throw new IllegalArgumentException("Empty or null stock list!");
- } else {
- List stocksFromFile = new ArrayList<>();
- List stockSymbols = new ArrayList<>();
+ }
+ List stocksFromFile = new ArrayList<>();
+ Set stockSymbols = new HashSet<>();
- validStocks.forEach(s -> {
- String[] lineElements = s.split(",");
- String stockSymbol = lineElements[0].trim();
- String stockName = lineElements[1].trim();
- BigDecimal stockPrice = new BigDecimal(lineElements[2].trim());
+ validStocks.forEach(s -> {
+ String[] lineElements = s.split(",");
+ String stockSymbol = lineElements[0].trim();
+ String stockName = lineElements[1].trim();
+ BigDecimal stockPrice = new BigDecimal(lineElements[2].trim());
- try {
- Stock stockObject = new Stock(stockSymbol, stockName, stockPrice);
- if (!stockSymbols.contains(stockSymbol)) {
- stockSymbols.add(stockSymbol);
- stocksFromFile.add(stockObject);
- }
- } catch (IllegalArgumentException e) {
- System.err.println("(" + s + ") is not a valid stock! Skipping...");
+ try {
+ Stock stockObject = new Stock(stockSymbol, stockName, stockPrice);
+ if (stockSymbols.add(stockSymbol)) {
+ stocksFromFile.add(stockObject);
}
-
- });
- return stocksFromFile;
+ } catch (IllegalArgumentException _) {
+ // Ignore invalid strings.
+ }
+ });
+ if (stocksFromFile.isEmpty()) {
+ throw new IllegalArgumentException("No stocks parsed succesfully!");
}
+ return stocksFromFile;
}
/**
@@ -81,13 +83,21 @@ public List getStocksFromStrings(final List validStocks)
public List stocksToStrings(final List stocks) {
if (stocks == null || stocks.isEmpty()) {
throw new IllegalArgumentException("Empty or null stock list!");
- } else {
- ArrayList stringList = new ArrayList<>();
- stocks.forEach(s ->
- stringList.add(s.getSymbol().trim() + "," + s.getCompany().trim()
- + "," + s.getSalesPrice().toString())
+ }
+
+ List stringList = new ArrayList<>();
+ for (Stock s : stocks) {
+ if (s == null) {
+ continue;
+ }
+
+ String csvRow = String.format("%s, %s, %s",
+ s.getSymbol().trim(),
+ s.getCompany().trim(),
+ s.getSalesPrice().toPlainString()
);
- return stringList;
+ stringList.add(csvRow);
}
+ return stringList;
}
}
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/TransactionFactory.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/TransactionFactory.java
index 733b9c5..62e849c 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/TransactionFactory.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/TransactionFactory.java
@@ -23,26 +23,23 @@ private TransactionFactory() {
* @param transactionType the type of transaction to create.
* @param share the share this transaction is about.
* @param week the week this transaction takes place in.
- * @param calculator the calculator to use when calculating the transaction.
*
* @return an implementation of {@link Transaction}.
* */
public static Transaction createTransaction(final TransactionType
transactionType,
final Share share,
- final int week,
- final TransactionCalculator
- calculator)
+ final int week)
throws IllegalArgumentException {
if (transactionType == null
|| share == null
- || !Validator.VALID_POSITIVE_INT.isValid(Integer.toString(week))
- || calculator == null) {
+ || !Validator.VALID_WEEK.isValid(Integer.toString(week))
+ ) {
throw new IllegalArgumentException("Null or empty parameters for factory!");
} else {
return switch (transactionType) {
- case SALE -> new Sale(share, week, calculator);
- case PURCHASE -> new Purchase(share, week, calculator);
+ case SALE -> new Sale(share, week, new SaleCalculator(share));
+ case PURCHASE -> new Purchase(share, week, new PurchaseCalculator(share));
default -> throw new
IllegalArgumentException("Invalid transaction type!");
};
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventChannel.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventChannel.java
index e67e360..215547e 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventChannel.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventChannel.java
@@ -8,11 +8,4 @@
* Decreases coupling and enables testing of event types.
* */
public interface EventChannel {
-
- /**
- * Getter method for enum name.
- *
- * @return String name of enum.
- * */
- String getName();
}
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventManager.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventManager.java
index a4a2bad..79e4f7b 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventManager.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventManager.java
@@ -32,9 +32,20 @@ public final class EventManager {
* @param subscriber the {@link EventSubscriber} object to add.
* @param eventChannel the {@link EventChannel} type this subscriber should subscribe to.
*
- *
+ * @throws IllegalArgumentException if subscriber is already subscribed to event channel,
+ * or if parameters are null.
*/
- public void addSubscriber(final EventSubscriber subscriber, final EventChannel eventChannel) {
+ public void addSubscriber(final EventSubscriber subscriber, final EventChannel eventChannel)
+ throws IllegalArgumentException {
+ if (subscriber == null || eventChannel == null) {
+ throw new IllegalArgumentException("Parameters cannot be null!");
+ }
+
+ List subscribers = subscriberMap.get(eventChannel);
+ if (subscribers != null && subscribers.contains(subscriber)) {
+ throw new IllegalArgumentException("Subscriber already subscribed to event channel!");
+ }
+
subscriberMap.computeIfAbsent(eventChannel, k -> new ArrayList<>()).add(subscriber);
}
@@ -49,9 +60,9 @@ public void addSubscriber(final EventSubscriber subscriber, final EventChannel e
*/
public void invokeEvent(final EventData data)
throws IllegalArgumentException {
- if (data == null || !subscriberMap.containsKey(data.channel())) {
+ if (data == null || data.data() == null) {
throw new IllegalArgumentException(
- "No subscriber listening to this event!"
+ "Data cannot be null!"
);
} else {
for (EventSubscriber e : subscriberMap.get(data.channel())) {
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventType.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventType.java
index ace4933..215ff37 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventType.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventType.java
@@ -51,12 +51,4 @@ public enum EventType implements EventChannel {
*
*/
SELECT_STOCK_FOR_MINIGAME;
-
- /**
- * {@inheritDoc}
- * */
- @Override
- public String getName() {
- return this.name();
- }
}
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/package-info.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/package-info.java
index 633c60a..10593f1 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/package-info.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/package-info.java
@@ -1,5 +1,5 @@
/**
* Contains classes providing modular functionality to the application,
- * such as the {@link edu.ntnu.idi.idatt2003.g40.mappe.service.FileConverter}.
+ * such as the {@link edu.ntnu.idi.idatt2003.g40.mappe.service.StockFileParser}.
* */
package edu.ntnu.idi.idatt2003.g40.mappe.service;
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/utils/Validator.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/utils/Validator.java
index 6d12cbc..6e1830b 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/utils/Validator.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/utils/Validator.java
@@ -1,7 +1,5 @@
package edu.ntnu.idi.idatt2003.g40.mappe.utils;
-import java.time.LocalDate;
-import java.time.format.DateTimeParseException;
import java.util.function.Predicate;
/**
@@ -55,26 +53,14 @@ public enum Validator {
/**
* Rule that checks if a string is considered a valid stock name.
* */
- VALID_STOCK_NAME(NOT_EMPTY.validationRule.and(s ->
- s.length() == 4), "Invalid stock name!"),
+ VALID_STOCK_SYMBOL(NOT_EMPTY.validationRule.and(s ->
+ s.length() == 4), "Invalid stock symbol!"),
/**
- * Rule that checks if a string represents a positive integer.
+ * Rule that checks if a string represents a valid week. (Greater than 1).
* */
- VALID_POSITIVE_INT(VALID_INT.validationRule.and(s ->
- Integer.parseInt(s) >= 0), "Number is not positive!"),
- /**
- * Rule that checks if string is not empty,
- * and if it can be parsed into a {@link LocalDate} object.
- */
- VALID_DATE(NOT_EMPTY.validationRule.and(s -> {
- try {
- LocalDate.parse(s);
- return true;
- } catch (DateTimeParseException e) {
- return false;
- }
- }), "Invalid Date!");
+ VALID_WEEK(VALID_INT.validationRule.and(s ->
+ Integer.parseInt(s) > 0), "Invalid week");
/** The predicate field set when creating constants. */
private final Predicate validationRule;
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewElement.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewElement.java
index 11e91cd..77e7d8b 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewElement.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewElement.java
@@ -1,6 +1,5 @@
package edu.ntnu.idi.idatt2003.g40.mappe.view;
-import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventData;
import edu.ntnu.idi.idatt2003.g40.mappe.utils.Validator;
import java.util.EnumMap;
import java.util.Map;
@@ -47,14 +46,18 @@ public abstract class ViewElement> {
* @param rootPane an instance of type T (defined in the class).
* @param viewName The name of the view as an {@link ViewEnum}.
*
+ * @throws IllegalArgumentException if parameters are invalid.
+ *
*/
- protected ViewElement(final T rootPane, final ViewEnum viewName, final Class actionEnum) {
+ protected ViewElement(final T rootPane,
+ final ViewEnum viewName,
+ final Class actionEnum)
+ throws IllegalArgumentException {
this(rootPane, actionEnum);
- if (Validator.NOT_EMPTY.isValid(viewName.name())) {
- this.viewName = viewName;
- } else {
+ if (!Validator.NOT_EMPTY.isValid(viewName.name())) {
throw new IllegalArgumentException(Validator.NOT_EMPTY.getErrorMessage());
}
+ this.viewName = viewName;
}
/**
@@ -62,16 +65,17 @@ protected ViewElement(final T rootPane, final ViewEnum viewName, final Class
*
* @param rootPane the root of this view.
*
+ * @throws IllegalArgumentException if parameters are null.
*/
- protected ViewElement(final T rootPane, final Class actionEnum) {
- if (rootPane != null) {
- setRootPane(rootPane);
- this.buttonMap = new EnumMap<>(actionEnum);
- initLayout();
- initStyling();
- } else {
+ protected ViewElement(final T rootPane, final Class actionEnum)
+ throws IllegalArgumentException {
+ if (rootPane == null || actionEnum == null) {
throw new IllegalArgumentException("Invalid ViewElement!");
}
+ setRootPane(rootPane);
+ this.buttonMap = new EnumMap<>(actionEnum);
+ initLayout();
+ initStyling();
}
/**
@@ -84,16 +88,6 @@ public ViewEnum getViewName() {
return viewName;
}
- /**
- * Setter method for the view name.
- *
- * @param name the new name to set this view element to.
- *
- */
- protected void setViewName(final ViewEnum name) {
- viewName = name;
- }
-
/**
* Getter method for the root pane.
*
@@ -155,21 +149,10 @@ public void setOnAction(final A action, final Runnable logic)
}
}
- /**
- * Method that defines how view elements set data.
- *
- * @param The type of data to set.
- * @param data the data to set.
- *
- */
- public void setData(final T2 data) {
- setViewName(data.getSceneName());
- }
-
/**
* Method called when updating a view.
* */
public void onUpdate() {
-
+ // Empty by default.
}
}
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewManager.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewManager.java
index b3ce064..45da26b 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewManager.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewManager.java
@@ -108,6 +108,7 @@ public void setScene(final ViewElement, ?> viewElement)
);
}
currentView = viewElement;
+ viewElement.onUpdate();
}
}
@@ -128,12 +129,10 @@ public void setScene(final ViewElement, ?> viewElement)
public void setScene(final ViewData data) throws IllegalArgumentException {
if (data == null) {
throw new IllegalArgumentException("Data is null!");
- } else {
- ViewElement, ?> viewElement = viewMap.get(data.getSceneName());
- viewElement.setData(data);
- setScene(viewElement);
- currentView = viewElement;
}
+ ViewElement, ?> viewElement = viewMap.get(data.getSceneName());
+ setScene(viewElement);
+ currentView = viewElement;
}
/**
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/creategame/CreateGameView.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/creategame/CreateGameView.java
index a64bec2..48550ac 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/creategame/CreateGameView.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/creategame/CreateGameView.java
@@ -302,12 +302,6 @@ protected void initStyling() {
createGameButton.setDisable(true);
}
- /** {@inheritDoc} */
- @Override
- public void onUpdate() {
- resetFields();
- }
-
/**
* Refreshes the highlight on the two stock-source buttons so the
* currently-active choice stands out from the inactive one.
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/dashboard/DashBoardController.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/dashboard/DashBoardController.java
index 9d31d0d..8bc66c5 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/dashboard/DashBoardController.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/dashboard/DashBoardController.java
@@ -116,7 +116,7 @@ private void populateStockList(final String filter) {
getViewElement().setOnStockAction(stockBtn, s, (Stock stock) -> {
BigDecimal amountOfSharesOwned = player.getPortfolio()
- .getTotalSharesBySymbol(s.getSymbol());
+ .getTotalShareQuantityBySymbol(s.getSymbol());
handleStockSelection(stock, amountOfSharesOwned.floatValue());
});
}
@@ -149,15 +149,13 @@ protected void initInteractions() {
getViewElement().setOnAction(DashBoardActions.SELL_SHARES, () -> {
if (Validator.NOT_EMPTY.isValid(getViewElement().getQuantityInputField().getText())
&& Float.parseFloat(getViewElement().getQuantityInputField().getText()) > 0) {
- List transactions = exchange.sell(
+ Transaction sale = exchange.sell(
new BigDecimal(getViewElement().getQuantityInputField().getText()),
getViewElement().getCurrentStock().getSymbol(),
player);
- for (Transaction t : transactions) {
- if(t.isCommited()) {
- getViewElement().addOwnedShares(-t.getShare().getQuantity().floatValue());
- }
+ if(sale.isCommited()) {
+ getViewElement().addOwnedShares(-sale.getShare().getQuantity().floatValue());
}
}
});
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/market/MarketController.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/market/MarketController.java
index e185fb3..86838a9 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/market/MarketController.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/market/MarketController.java
@@ -84,7 +84,7 @@ protected void initInteractions() {
* @return the total quantity, or 0 if the symbol is invalid.
* */
private int ownedQuantity(final String symbol) {
- if (!Validator.VALID_STOCK_NAME.isValid(symbol)) {
+ if (!Validator.VALID_STOCK_SYMBOL.isValid(symbol)) {
return 0;
}
return player.getPortfolio().getShares(symbol).stream()
diff --git a/src/main/resources/testStockData.txt b/src/main/resources/testStockData.txt
deleted file mode 100644
index b349d0e..0000000
--- a/src/main/resources/testStockData.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-#THIS IS A COMMENT.
-
-AAPL, Apple Inc., 276.43
-NVID, Nvidida Corporation, 241.591
-
-#Above me are some valid formats.
-#Below me are some invalid formats
-
-COOLI, This is a cool name, 252.2
-
-COOL, This is a cool name, 252.2a
\ No newline at end of file
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/ExchangeTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/ExchangeTest.java
index 8f4f1a5..66ce0c5 100644
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/ExchangeTest.java
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/ExchangeTest.java
@@ -1,116 +1,262 @@
package edu.ntnu.idi.idatt2003.g40.mappe.engine;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import edu.ntnu.idi.idatt2003.g40.mappe.model.Player;
+import edu.ntnu.idi.idatt2003.g40.mappe.model.Purchase;
+import edu.ntnu.idi.idatt2003.g40.mappe.model.Sale;
+import edu.ntnu.idi.idatt2003.g40.mappe.model.Share;
+import edu.ntnu.idi.idatt2003.g40.mappe.model.Stock;
+import edu.ntnu.idi.idatt2003.g40.mappe.model.Transaction;
+import edu.ntnu.idi.idatt2003.g40.mappe.service.PurchaseCalculator;
+import edu.ntnu.idi.idatt2003.g40.mappe.service.SaleCalculator;
+import edu.ntnu.idi.idatt2003.g40.mappe.service.TransactionCalculator;
import java.math.BigDecimal;
+import java.util.ArrayList;
import java.util.List;
-
-import edu.ntnu.idi.idatt2003.g40.mappe.model.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
final class ExchangeTest {
+ /**
+ * Test sample stock.
+ * */
private Stock appleStock;
+ /**
+ * Test sample stock.
+ * */
+ private Stock teslaStock;
+
+ /**
+ * Test sample stock.
+ * */
+ private Stock pearStock;
+
+ /**
+ * Test exchange to use.
+ * */
+ private Exchange testExchange;
+
+ /**
+ * Test player for testing transactions.
+ * */
+ private Player testPlayer;
+
@BeforeEach
void setUp() {
appleStock = new Stock("AAPL", "Apple", new BigDecimal("100"));
+ teslaStock = new Stock("TSLA", "Tesla", new BigDecimal("200"));
+ pearStock = new Stock("Pear", "Pear Inc.", new BigDecimal("97"));
+ testExchange = new Exchange("NASDAQ",
+ List.of(appleStock, teslaStock, pearStock));
+ testPlayer = new Player("Alice", new BigDecimal("1000"));
}
@Test
void constructorSetsNameWeekAndStocksCorrectly() {
- Stock tesla = new Stock("TSLA", "Tesla", new BigDecimal("200"));
+ assertEquals("NASDAQ", testExchange.getName());
+ assertEquals(1, testExchange.getWeek());
+ assertEquals(1, testExchange.weekProperty().intValue());
+ assertTrue(testExchange.hasStock("AAPL"));
+ assertTrue(testExchange.hasStock("TSLA"));
+ }
+
+ @Test
+ void constructorThrowsErrorOnInvalidParameters() {
+ assertDoesNotThrow(() ->
+ new Exchange("Valid name",
+ List.of(appleStock, teslaStock, pearStock)));
+
+ assertThrows(IllegalArgumentException.class, () ->
+ new Exchange(null, List.of(appleStock, teslaStock, pearStock)));
- Exchange exchange = new Exchange("NASDAQ", List.of(appleStock, tesla));
+ assertThrows(IllegalArgumentException.class, () ->
+ new Exchange("Valid name", null));
- assertEquals("NASDAQ", exchange.getName());
- assertEquals(1, exchange.getWeek());
- assertTrue(exchange.hasStock("AAPL"));
- assertTrue(exchange.hasStock("TSLA"));
+ assertThrows(IllegalArgumentException.class, () ->
+ new Exchange("Valid name", new ArrayList<>()));
}
@Test
void getStockReturnsCorrectStock() {
- Exchange exchange = new Exchange("NASDAQ", List.of(appleStock));
-
- Stock result = exchange.getStock("AAPL");
+ Stock result = testExchange.getStock("AAPL");
assertSame(appleStock, result);
}
@Test
- void findStocksReturnsMatchingStocksBySymbolOrCompany() {
- Stock tesla = new Stock("TSLA", "Tesla", new BigDecimal("200"));
- Exchange exchange = new Exchange("NASDAQ", List.of(appleStock, tesla));
+ void getStockThrowsExceptionOnInvalidStock() {
+ assertDoesNotThrow(
+ () -> testExchange.getStock("AAPL")
+ );
+ assertThrows(IllegalArgumentException.class,
+ () -> testExchange.getStock("INVALIDSYMBOL"));
+ assertThrows(IllegalArgumentException.class,
+ () -> testExchange.getStock("NVID"));
+ }
- List resultBySymbol = exchange.findStocks("AAP");
- List resultByCompany = exchange.findStocks("tes");
+ @Test
+ void findStocksReturnsMatchingStocksBySymbolOrCompany() {
+ List resultBySymbol = testExchange.findStocks("AAP");
+ List resultByCompany = testExchange.findStocks("tes");
assertEquals(1, resultBySymbol.size());
assertTrue(resultBySymbol.contains(appleStock));
assertEquals(1, resultByCompany.size());
- assertTrue(resultByCompany.contains(tesla));
+ assertTrue(resultByCompany.contains(teslaStock));
}
@Test
void buyReturnsPurchaseAndWithdrawsMoneyFromPlayer() {
- Exchange exchange = new Exchange("NASDAQ", List.of(appleStock));
- Player player = new Player("Alice", new BigDecimal("1000"));
+ Transaction transaction =
+ testExchange.buy("AAPL", new BigDecimal("2"), testPlayer);
- Transaction transaction = exchange.buy("AAPL", new BigDecimal("2"), player);
+ TransactionCalculator calculator =
+ new PurchaseCalculator(transaction.getShare());
+
+ BigDecimal expectedPlayerMoney =
+ testPlayer.getStartingMoney().subtract(calculator.calculateTotal());
assertInstanceOf(Purchase.class, transaction);
assertEquals(1, transaction.getWeek());
assertEquals(new BigDecimal("2"), transaction.getShare().getQuantity());
- assertEquals(new BigDecimal("100"), transaction.getShare()
+ assertEquals(appleStock.getSalesPrice(), transaction.getShare()
.getPurchasePrice());
- assertEquals(new BigDecimal("799.000"), player.getMoney());
+ assertEquals(expectedPlayerMoney, testPlayer.getMoney());
+ }
+
+ @Test
+ void buyThrowsExceptionOnIllegalArguments() {
+ assertDoesNotThrow(() ->
+ testExchange.buy("AAPL", new BigDecimal("2"), testPlayer));
+
+ assertThrows(IllegalArgumentException.class, () ->
+ testExchange.buy("NVID", new BigDecimal("2"), testPlayer));
+
+ assertThrows(IllegalArgumentException.class, () ->
+ testExchange.buy(null, new BigDecimal("2"), testPlayer));
+
+ assertThrows(IllegalArgumentException.class, () ->
+ testExchange.buy("AAPL", null, testPlayer));
+
+ assertThrows(IllegalArgumentException.class, () ->
+ testExchange.buy("AAPL", new BigDecimal("2"), null));
+
}
@Test
void sellReturnsSaleAndAddsMoneyToPlayer() {
- Stock apple = new Stock("AAPL", "Apple", new BigDecimal("150"));
- Exchange exchange = new Exchange("NASDAQ", List.of(apple));
- Player player = new Player("Alice", new BigDecimal("1000"));
- Share share = new Share(apple, new BigDecimal("2"), new BigDecimal("100"));
+ Share share =
+ new Share(appleStock, new BigDecimal("2"),
+ appleStock.getSalesPrice());
- Transaction transaction = exchange.sell(share, player);
+ Transaction transaction = testExchange.sell(share, testPlayer);
+
+ TransactionCalculator calculator = new SaleCalculator(share);
+ BigDecimal expectedPlayerMoney =
+ testPlayer.getStartingMoney().add(calculator.calculateTotal());
assertInstanceOf(Sale.class, transaction);
assertEquals(1, transaction.getWeek());
assertSame(share, transaction.getShare());
- assertEquals(new BigDecimal("1267.9000"), player.getMoney());
+ assertEquals(expectedPlayerMoney, testPlayer.getMoney());
+ }
+
+ @Test
+ void sellShareObjectThrowsExceptionOnIllegalArguments() {
+ Transaction transaction = testExchange.buy("AAPL", new BigDecimal("2"), testPlayer);
+ Share shareObject = transaction.getShare();
+ assertDoesNotThrow(() ->
+ testExchange.sell(shareObject, testPlayer));
+
+ assertThrows(IllegalArgumentException.class, () ->
+ testExchange.sell(null, testPlayer));
+
+ assertThrows(IllegalArgumentException.class, () ->
+ testExchange.sell(shareObject, null));
+ }
+
+ @Test
+ void sellingSharesBasedOnAmountFunctionsAsIntended() {
+ testExchange.buy(appleStock.getSymbol(), new BigDecimal("3"), testPlayer);
+
+ Transaction sale = testExchange.sell(new BigDecimal("1.5"), appleStock.getSymbol(), testPlayer);
+
+ assertInstanceOf(Sale.class, sale);
+ BigDecimal expectedPlayerMoney = new BigDecimal("847.000");
+ assertEquals(expectedPlayerMoney, testPlayer.getMoney());
+
+ BigDecimal actualQuantity = testPlayer.getPortfolio().getShares(appleStock.getSymbol()).getFirst().getQuantity();
+ BigDecimal expectedRemainingShares = new BigDecimal("1.5");
+ assertEquals(0, expectedRemainingShares.compareTo(actualQuantity));
+ }
+
+ @Test
+ void attemptingToSellMoreSharesThanOwnedSellsAllSharesOwned() {
+ testExchange.buy(appleStock.getSymbol(),
+ new BigDecimal("3.12"), testPlayer);
+ testExchange.buy(appleStock.getSymbol(),
+ new BigDecimal("4.56"), testPlayer);
+
+ testExchange.sell(new BigDecimal("100.00"),
+ appleStock.getSymbol(), testPlayer);
+
+ BigDecimal expectedPlayerMoney = new BigDecimal("988.48000");
+ assertEquals(0, expectedPlayerMoney.compareTo(testPlayer.getMoney()));
+
+ assertTrue(testPlayer.getPortfolio().getShares(appleStock.getSymbol()).isEmpty());
+ }
+
+ @Test
+ void attemptingToSellPartialAmountOfSharesWithStockNotOwnedByPlayerThrowsError() {
+ BigDecimal testAmount = new BigDecimal("6.00");
+ assertThrows(IllegalArgumentException.class,
+ () -> testExchange.sell(testAmount,
+ appleStock.getSymbol(), testPlayer));
+ }
+
+ @Test
+ void attemptingToSellPartialAmountOfSharesWithInvalidNumberAndPlayerThrowsError() {
+ BigDecimal testAmount = new BigDecimal("6.00");
+ testExchange.buy(appleStock.getSymbol(), testAmount, testPlayer);
+
+ assertDoesNotThrow(() -> testExchange.sell(testAmount,
+ appleStock.getSymbol(), testPlayer));
+ assertThrows(IllegalArgumentException.class,
+ () -> testExchange.sell(null, appleStock.getSymbol(), testPlayer));
+ assertThrows(IllegalArgumentException.class,
+ () -> testExchange.sell(testAmount, null, testPlayer));
+ assertThrows(IllegalArgumentException.class,
+ () -> testExchange.sell(testAmount, appleStock.getSymbol(), null));
}
@Test
void advanceIncreasesWeekAndUpdatesStockPrice() {
- Exchange exchange = new Exchange("NASDAQ", List.of(appleStock));
BigDecimal oldPrice = appleStock.getSalesPrice();
- exchange.advance();
+ testExchange.advance();
- assertEquals(2, exchange.getWeek());
+ assertEquals(2, testExchange.getWeek());
assertNotEquals(oldPrice, appleStock.getSalesPrice());
}
@Test
void getGainersActuallyReturnsProperGainers() {
- Stock teslaStock = new Stock("TSLA", "Tesla", new BigDecimal("200.00"));
- Stock pearStock = new Stock("PEAR", "Pear inc.", new BigDecimal("97.00"));
-
appleStock.addNewSalesPrice(new BigDecimal("150.00"));
teslaStock.addNewSalesPrice(new BigDecimal("230.00"));
pearStock.addNewSalesPrice(new BigDecimal("112.00"));
- Exchange exchange = new Exchange("Exchange", List.of(appleStock, teslaStock, pearStock));
-
- List actualGainers = exchange.getGainers(2);
+ List actualGainers = testExchange.getGainers(2);
boolean actualGainersContainLimitedGainers = actualGainers.contains(teslaStock)
&& actualGainers.contains(appleStock);
@@ -124,17 +270,23 @@ void getGainersActuallyReturnsProperGainers() {
}
@Test
- void getLosersActuallyReturnsProperLosers() {
- Stock teslaStock = new Stock("TSLA", "Tesla", new BigDecimal("200.00"));
- Stock pearStock = new Stock("PEAR", "Pear inc.", new BigDecimal("97.00"));
+ void getGainersDoesNotReturnLoser() {
+
+ appleStock.addNewSalesPrice(new BigDecimal("150.00"));
+ teslaStock.addNewSalesPrice(new BigDecimal("230.00"));
+ pearStock.addNewSalesPrice(new BigDecimal("20"));
+ List actualGainers = testExchange.getGainers(3);
+
+ assertFalse(actualGainers.contains(pearStock));
+ }
+ @Test
+ void getLosersActuallyReturnsProperLosers() {
appleStock.addNewSalesPrice(new BigDecimal("50.00"));
teslaStock.addNewSalesPrice(new BigDecimal("170.00"));
- pearStock.addNewSalesPrice(new BigDecimal("82.00"));
+ pearStock.addNewSalesPrice(new BigDecimal("87"));
- Exchange exchange = new Exchange("Exchange", List.of(appleStock, teslaStock, pearStock));
-
- List actualLosers = exchange.getLosers(2);
+ List actualLosers = testExchange.getLosers(2);
boolean actualLosersContainsValidLosers = actualLosers.contains(teslaStock)
&& actualLosers.contains(appleStock);
@@ -146,4 +298,29 @@ void getLosersActuallyReturnsProperLosers() {
boolean actualLosersNotContainingLoserOutsideOfLimit = !actualLosers.contains(pearStock);
assertTrue(actualLosersNotContainingLoserOutsideOfLimit);
}
+
+ @Test
+ void getLosersDoesNotReturnWinners() {
+
+ appleStock.addNewSalesPrice(new BigDecimal("50.00"));
+ teslaStock.addNewSalesPrice(new BigDecimal("170.00"));
+ pearStock.addNewSalesPrice(new BigDecimal("200"));
+
+ List actualLosers = testExchange.getLosers(3);
+
+ boolean actualLosersNotContainsWinner = !actualLosers.contains(pearStock);
+ assertTrue(actualLosersNotContainsWinner);
+ }
+
+ @Test
+ void invalidLimitForGettingGainersThrowError() {
+ assertThrows(IllegalArgumentException.class, () -> testExchange.getGainers(0));
+ assertThrows(IllegalArgumentException.class, () -> testExchange.getGainers(-1));
+ }
+
+ @Test
+ void invalidLimitForGettingLosersThrowError() {
+ assertThrows(IllegalArgumentException.class, () -> testExchange.getLosers(0));
+ assertThrows(IllegalArgumentException.class, () -> testExchange.getLosers(-1));
+ }
}
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/TransactionArchiveTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/TransactionArchiveTest.java
index 091daeb..fce1960 100644
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/TransactionArchiveTest.java
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/TransactionArchiveTest.java
@@ -1,141 +1,163 @@
package edu.ntnu.idi.idatt2003.g40.mappe.engine;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import edu.ntnu.idi.idatt2003.g40.mappe.model.Purchase;
+import edu.ntnu.idi.idatt2003.g40.mappe.model.Sale;
+import edu.ntnu.idi.idatt2003.g40.mappe.model.Share;
+import edu.ntnu.idi.idatt2003.g40.mappe.model.Stock;
+import edu.ntnu.idi.idatt2003.g40.mappe.model.Transaction;
+import edu.ntnu.idi.idatt2003.g40.mappe.service.TransactionFactory;
+import edu.ntnu.idi.idatt2003.g40.mappe.service.TransactionType;
import java.math.BigDecimal;
import java.util.List;
-
-import edu.ntnu.idi.idatt2003.g40.mappe.model.*;
-import edu.ntnu.idi.idatt2003.g40.mappe.service.TransactionCalculator;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
final class TransactionArchiveTest {
/**
- * Transaction calculator to be used for testing.
+ * Test transactionArchive.
* */
- private final TransactionCalculator calculator = new TransactionCalculator() {
- @Override
- public BigDecimal calculateGross() {
- return BigDecimal.ZERO;
- }
-
- @Override
- public BigDecimal calculateCommission() {
- return BigDecimal.ZERO;
- }
-
- @Override
- public BigDecimal calculateTax() {
- return BigDecimal.ZERO;
- }
-
- @Override
- public BigDecimal calculateTotal() {
- return BigDecimal.ZERO;
- }
- };
+ private TransactionArchive transactionArchive;
+
+ /**
+ * Sample share used to emulate transactions.
+ * */
+ private Share sampleShare;
+
+ @BeforeEach
+ void setUp() {
+ transactionArchive = new TransactionArchive();
+ Stock sampleStock = new Stock("AAPL", "Apple", new BigDecimal("100.00"));
+ sampleShare = new Share(sampleStock,
+ new BigDecimal("1.00"), sampleStock.getSalesPrice());
+ }
@Test
void newArchiveIsEmpty() {
- TransactionArchive archive = new TransactionArchive();
-
- assertTrue(archive.isEmpty());
+ assertTrue(transactionArchive.isEmpty());
}
@Test
- void addMakesArchiveNonEmpty() {
- TransactionArchive archive = new TransactionArchive();
- Transaction transaction = createPurchase("AAPL", "Apple", 1);
+ void addValidTransactionMakesArchiveNonEmpty() {
+ Transaction purchase = createPurchase(1);
- boolean result = archive.add(transaction);
+ boolean result = transactionArchive.add(purchase);
assertTrue(result);
- assertFalse(archive.isEmpty());
+ assertFalse(transactionArchive.isEmpty());
}
@Test
- void getTransactionsReturnsOnlyTransactionsFromGivenWeek() {
- TransactionArchive archive = new TransactionArchive();
+ void addNullTransactionThrowsException() {
+ Transaction purchase = createPurchase(1);
+ assertDoesNotThrow(() -> transactionArchive.add(purchase));
+ assertThrows(IllegalArgumentException.class,
+ () -> transactionArchive.add(null));
+ }
- Transaction transaction1 = createPurchase("AAPL", "Apple", 1);
- Transaction transaction2 = createSale("TSLA", "Tesla", 2);
- Transaction transaction3 = createPurchase("NVDA", "Nvidia", 1);
+ @Test
+ void genericGetTransactionsGetsAllTransactions() {
+ Transaction week1Purchase1 = createPurchase(1);
+ Transaction week1Purchase2 = createPurchase(1);
+ Transaction week2Purchase = createPurchase(2);
+ Transaction week4Sale = createPurchase(4);
+
+ transactionArchive.add(week1Purchase1);
+ transactionArchive.add(week1Purchase2);
+ transactionArchive.add(week2Purchase);
+ transactionArchive.add(week4Sale);
+
+ List result = transactionArchive.getTransactions();
+
+ assertEquals(4, result.size());
+ assertTrue(result.contains(week1Purchase1));
+ assertTrue(result.contains(week1Purchase2));
+ assertTrue(result.contains(week2Purchase));
+ assertTrue(result.contains(week4Sale));
+ }
- archive.add(transaction1);
- archive.add(transaction2);
- archive.add(transaction3);
+ @Test
+ void getTransactionsWithWeekSpecifierReturnsOnlyTransactionsFromGivenWeek() {
+ Transaction week1Purchase1 = createPurchase(1);
+ Transaction week1Purchase2 = createPurchase(1);
+ Transaction week2Purchase = createPurchase(2);
+
+ transactionArchive.add(week1Purchase1);
+ transactionArchive.add(week1Purchase2);
+ transactionArchive.add(week2Purchase);
- List result = archive.getTransactions(1);
+ List result = transactionArchive.getTransactions(1);
assertEquals(2, result.size());
- assertTrue(result.contains(transaction1));
- assertTrue(result.contains(transaction3));
+ assertTrue(result.contains(week1Purchase1));
+ assertTrue(result.contains(week1Purchase2));
+ assertFalse(result.contains(week2Purchase));
}
@Test
- void getPurchasesReturnsOnlyPurchasesFromGivenWeek() {
- TransactionArchive archive = new TransactionArchive();
+ void getTransactionsWithWeekSpecifierInvalidWeekThrowsException() {
+ assertDoesNotThrow(() -> transactionArchive.getTransactions(1));
+ assertThrows(IllegalArgumentException.class,
+ () -> transactionArchive.getTransactions(0));
+ }
- Purchase purchase1 = createPurchase("AAPL", "Apple", 1);
- Purchase purchase2 = createPurchase("NVDA", "Nvidia", 2);
- Sale sale = createSale("TSLA", "Tesla", 1);
+ @Test
+ void getPurchasesReturnsOnlyPurchasesFromGivenWeek() {
+ Transaction week1Purchase = createPurchase(1);
+ Transaction week2Purchase = createPurchase(2);
+ Transaction week1Sale = createSale(1);
- archive.add(purchase1);
- archive.add(purchase2);
- archive.add(sale);
+ transactionArchive.add(week1Purchase);
+ transactionArchive.add(week2Purchase);
+ transactionArchive.add(week1Sale);
- List result = archive.getPurchases(1);
+ List result = transactionArchive.getPurchases(1);
assertEquals(1, result.size());
- assertTrue(result.contains(purchase1));
+ assertTrue(result.contains(week1Purchase));
}
@Test
void getSalesReturnsOnlySalesFromGivenWeek() {
- TransactionArchive archive = new TransactionArchive();
+ Transaction week1Sale = createSale(1);
+ Transaction week3Sale = createSale(3);
+ Transaction week1Purchase = createPurchase(1);
- Sale sale1 = createSale("TSLA", "Tesla", 1);
- Sale sale2 = createSale("NVDA", "Nvidia", 2);
- Purchase purchase = createPurchase("AAPL", "Apple", 1);
+ transactionArchive.add(week1Sale);
+ transactionArchive.add(week3Sale);
+ transactionArchive.add(week1Purchase);
- archive.add(sale1);
- archive.add(sale2);
- archive.add(purchase);
-
- List result = archive.getSales(1);
+ List result = transactionArchive.getSales(1);
assertEquals(1, result.size());
- assertTrue(result.contains(sale1));
+ assertTrue(result.contains(week1Sale));
}
@Test
void countDistinctWeeksCountsUniqueWeeksOnly() {
- TransactionArchive archive = new TransactionArchive();
-
- archive.add(createPurchase("AAPL", "Apple", 1));
- archive.add(createSale("TSLA", "Tesla", 1));
- archive.add(createPurchase("NVDA", "Nvidia", 2));
- archive.add(createSale("META", "Meta", 3));
+ transactionArchive.add(createPurchase(1));
+ transactionArchive.add(createPurchase(1));
+ transactionArchive.add(createPurchase(2));
+ transactionArchive.add(createSale(3));
- assertEquals(3, archive.countDistinctWeeks());
+ assertEquals(3, transactionArchive.countDistinctWeeks());
}
- private Purchase createPurchase(final String symbol,
- final String company,
- final int week) {
- Stock stock = new Stock(symbol, company, new BigDecimal("100"));
- Share share = new Share(stock, BigDecimal.ONE, new BigDecimal("100"));
- return new Purchase(share, week, calculator);
+ private Transaction createPurchase(final int week) {
+ return TransactionFactory.createTransaction(
+ TransactionType.PURCHASE, sampleShare, week
+ );
}
- private Sale createSale(final String symbol,
- final String company,
- final int week) {
- Stock stock = new Stock(symbol, company, new BigDecimal("100"));
- Share share = new Share(stock, BigDecimal.ONE, new BigDecimal("100"));
- return new Sale(share, week, calculator);
+ private Transaction createSale(final int week) {
+ return TransactionFactory.createTransaction(
+ TransactionType.SALE, sampleShare, week
+ );
}
}
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PlayerStatusTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PlayerStatusTest.java
index 1765d46..d5952dc 100644
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PlayerStatusTest.java
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PlayerStatusTest.java
@@ -31,7 +31,7 @@ void getStatusAfterDoubleIncreaseReturnsBetterStatus() {
@Test
void gettingStatusWhenNegativeDifferenceReturnsWorstStatus() {
- testPlayer.addMoney(new BigDecimal(-1000));
+ testPlayer.withdrawMoney(new BigDecimal(1000));
assertEquals(PlayerStatus.NOOB, testPlayer.getStatus());
}
@@ -40,10 +40,10 @@ void multipleChangesInValueCauseCorrectStatus() {
testPlayer.addMoney(new BigDecimal(2000));
assertEquals(PlayerStatus.PRO, testPlayer.getStatus());
- testPlayer.addMoney(new BigDecimal(-1000));
+ testPlayer.withdrawMoney(new BigDecimal(1000));
assertEquals(PlayerStatus.TRYHARD, testPlayer.getStatus());
- testPlayer.addMoney(new BigDecimal(-500));
+ testPlayer.withdrawMoney(new BigDecimal(500));
assertEquals(PlayerStatus.GOOD, testPlayer.getStatus());
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PlayerTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PlayerTest.java
index 70dd13b..9ac2d64 100644
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PlayerTest.java
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PlayerTest.java
@@ -1,65 +1,197 @@
package edu.ntnu.idi.idatt2003.g40.mappe.model;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import edu.ntnu.idi.idatt2003.g40.mappe.exceptions.NotEnoughMoneyException;
+import edu.ntnu.idi.idatt2003.g40.mappe.service.TransactionFactory;
+import edu.ntnu.idi.idatt2003.g40.mappe.service.TransactionType;
import java.math.BigDecimal;
-
-import edu.ntnu.idi.idatt2003.g40.mappe.service.SaleCalculator;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
final class PlayerTest {
+ /**
+ * Test player to use.
+ * */
+ private Player testPlayer;
+
+ @BeforeEach
+ void setUp() {
+ testPlayer = new Player("Alice", new BigDecimal("1000"));
+ }
+
@Test
void constructorSetsNameMoneyPortfolioAndArchive() {
- Player player = new Player("Alice", new BigDecimal("1000"));
+ assertEquals("Alice", testPlayer.getName());
+ assertEquals(new BigDecimal("1000"), testPlayer.getMoney());
+ assertNotNull(testPlayer.getPortfolio());
+ assertNotNull(testPlayer.getTransactionArchive());
+ assertEquals(1000f,
+ testPlayer.getMoneyAsFloatProperty().floatValue());
+ assertEquals(1000f,
+ testPlayer.getNetWorthAsFloatProperty().floatValue());
+ }
- assertEquals("Alice", player.getName());
- assertEquals(new BigDecimal("1000"), player.getMoney());
- assertNotNull(player.getPortfolio());
- assertNotNull(player.getTransactionArchive());
+ @Test
+ void constructorThrowsExceptionOnIllegalArguments() {
+ assertDoesNotThrow(
+ () -> new Player("Bob", new BigDecimal("2000"))
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new Player("", new BigDecimal("2000"))
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new Player(null, new BigDecimal("2000"))
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new Player("Bob", null)
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new Player("Bob", new BigDecimal("0"))
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new Player("Bob", new BigDecimal("-100"))
+ );
}
@Test
void addMoneyIncreasesBalance() {
- Player player = new Player("Bob", new BigDecimal("500"));
-
- player.addMoney(new BigDecimal("200"));
+ testPlayer.addMoney(new BigDecimal("200"));
+ assertEquals(new BigDecimal("1200"), testPlayer.getMoney());
+ }
- assertEquals(new BigDecimal("700"), player.getMoney());
+ @Test
+ void addMoneyThrowsExceptionWhenIllegalArguments() {
+ assertDoesNotThrow(
+ () -> testPlayer.addMoney(new BigDecimal("200"))
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> testPlayer.addMoney(null)
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> testPlayer.addMoney(new BigDecimal("-10"))
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> testPlayer.addMoney(new BigDecimal("0"))
+ );
}
@Test
void withdrawMoneyDecreasesBalance() {
- Player player = new Player("Charlie", new BigDecimal("500"));
-
- player.withdrawMoney(new BigDecimal("150"));
+ testPlayer.withdrawMoney(new BigDecimal("150"));
+ assertEquals(new BigDecimal("850"), testPlayer.getMoney());
+ }
- assertEquals(new BigDecimal("350"), player.getMoney());
+ @Test
+ void withdrawMoneyThrowsExceptionOnIllegalArguments() {
+ assertDoesNotThrow(
+ () -> testPlayer.withdrawMoney(new BigDecimal("200"))
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> testPlayer.withdrawMoney(null)
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> testPlayer.withdrawMoney(new BigDecimal("-10"))
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> testPlayer.withdrawMoney(new BigDecimal("0"))
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> testPlayer.withdrawMoney(new BigDecimal("99999"))
+ );
}
@Test
void addAndWithdrawMoneyUpdateBalanceCorrectly() {
- Player player = new Player("Dana", new BigDecimal("1000"));
+ testPlayer.addMoney(new BigDecimal("250"));
+ testPlayer.withdrawMoney(new BigDecimal("300"));
- player.addMoney(new BigDecimal("250"));
- player.withdrawMoney(new BigDecimal("300"));
-
- assertEquals(new BigDecimal("950"), player.getMoney());
+ assertEquals(new BigDecimal("950"), testPlayer.getMoney());
}
@Test
- void getNetWorthCalculatesCorrectly() {
+ void getNetWorthCalculatesCorrectlyForSales() {
Stock stock = new Stock("AAPL", "Apple inc.,", new BigDecimal("100.00"));
- Player player = new Player("Bob", new BigDecimal("900"));
Share share = new Share(stock, new BigDecimal("1"), new BigDecimal("100.00"));
- player.getPortfolio().addShare(share);
- SaleCalculator saleCalculator = new SaleCalculator(share);
+ BigDecimal expectedNetWorth = testPlayer.getNetWorth();
+ Transaction transaction = TransactionFactory.createTransaction(TransactionType.SALE, share, 1);
+
+ testPlayer.handleTransaction(transaction);
+
+ BigDecimal actualNetWorth = testPlayer.getNetWorth();
+ expectedNetWorth = expectedNetWorth.add(transaction.getCalculator().calculateTotal());
+
+ assertEquals(expectedNetWorth, actualNetWorth);
+ assertEquals(actualNetWorth.floatValue(),
+ testPlayer.getNetWorthAsFloatProperty().floatValue());
+ assertEquals(testPlayer.getMoney().floatValue(),
+ testPlayer.getMoneyAsFloatProperty().floatValue());
+ }
+
+ @Test
+ void getNetWorthCalculatesCorrectlyForPurchases() {
+ Stock stock = new Stock("AAPL", "Apple inc.,", new BigDecimal("100.00"));
+ Share share = new Share(stock, new BigDecimal("1"), stock.getSalesPrice());
+
+ BigDecimal expectedNetWorth = testPlayer.getNetWorth();
+ Transaction transaction = TransactionFactory.createTransaction(TransactionType.PURCHASE, share, 1);
+
+ testPlayer.handleTransaction(transaction);
+
+ BigDecimal actualNetWorth = testPlayer.getNetWorth();
+ expectedNetWorth = expectedNetWorth.subtract(
+ transaction.getCalculator().calculateCommission()
+ );
+
+ assertEquals(expectedNetWorth, actualNetWorth);
+ assertEquals(actualNetWorth.floatValue(),
+ testPlayer.getNetWorthAsFloatProperty().floatValue());
+ assertEquals(testPlayer.getMoney().floatValue(),
+ testPlayer.getMoneyAsFloatProperty().floatValue());
+ }
+
+ @Test
+ void handleTransactionThrowsExceptionsOnIllegalArgumentsAndNotEnoughMoney() {
+ Stock stock = new Stock("AAPL", "Apple inc.,", new BigDecimal("100.00"));
+ Share share = new Share(stock, new BigDecimal("1"), stock.getSalesPrice());
+
+ Share expensiveShare = new Share(stock, new BigDecimal("999.99"), new BigDecimal("999.99"));
+
+ Transaction validTransaction = TransactionFactory.createTransaction(
+ TransactionType.PURCHASE, share, 1
+ );
+
+ Transaction tooExpensiveTransaction = TransactionFactory.createTransaction(
+ TransactionType.PURCHASE, expensiveShare, 1
+ );
+
+ assertDoesNotThrow(
+ () -> testPlayer.handleTransaction(validTransaction)
+ );
- BigDecimal calculatedNetWorth = player.getMoney().add(saleCalculator.calculateTotal());
- BigDecimal actualNetWorth = player.getNetWorth();
+ assertThrows(IllegalArgumentException.class,
+ () -> testPlayer.handleTransaction(null)
+ );
- assertEquals(calculatedNetWorth, actualNetWorth);
+ assertThrows(NotEnoughMoneyException.class,
+ () -> testPlayer.handleTransaction(tooExpensiveTransaction)
+ );
}
}
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PortfolioTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PortfolioTest.java
index 470f26a..fde5639 100644
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PortfolioTest.java
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PortfolioTest.java
@@ -1,104 +1,152 @@
package edu.ntnu.idi.idatt2003.g40.mappe.model;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.math.BigDecimal;
import java.util.List;
-import edu.ntnu.idi.idatt2003.g40.mappe.service.SaleCalculator;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
final class PortfolioTest {
+ /**
+ * Test portfolio used.
+ * */
+ private Portfolio testPortfolio;
+
+ /**
+ * Test stock used.
+ * */
+ private Stock testStock;
+
+ /**
+ * Test share used.
+ * */
+ private Share testShare;
+
+ @BeforeEach
+ void setUp() {
+ testPortfolio = new Portfolio();
+ testStock = new Stock("AAPL", "Apple", new BigDecimal("150"));
+ testShare = new Share(testStock, new BigDecimal("2"), new BigDecimal("100"));
+ }
+
@Test
void addShareAddsShareToPortfolio() {
- Portfolio portfolio = new Portfolio();
- Stock stock = new Stock("AAPL", "Apple", new BigDecimal("150"));
- Share share = new Share(stock, new BigDecimal("2"), new BigDecimal("100"));
+ assertDoesNotThrow(
+ () -> testPortfolio.addShare(testShare));
- boolean result = portfolio.addShare(share);
+ assertTrue(testPortfolio.contains(testShare));
+ }
- assertTrue(result);
- assertTrue(portfolio.contains(share));
+ @Test
+ void addingNullShareThrowsException() {
+ assertThrows(IllegalArgumentException.class,
+ () -> testPortfolio.addShare(null));
+
+ assertFalse(testPortfolio.contains(testShare));
}
@Test
void removeShareRemovesShareFromPortfolio() {
- Portfolio portfolio = new Portfolio();
- Stock stock = new Stock("TSLA", "Tesla", new BigDecimal("200"));
- Share share = new Share(stock, new BigDecimal("1"), new BigDecimal("200"));
+ assertFalse(testPortfolio.contains(testShare));
+
+ testPortfolio.addShare(testShare);
- portfolio.addShare(share);
- boolean result = portfolio.removeShare(share);
+ assertTrue(testPortfolio.contains(testShare));
+
+ boolean result = testPortfolio.removeShare(testShare);
assertTrue(result);
- assertFalse(portfolio.contains(share));
+ assertFalse(testPortfolio.contains(testShare));
}
@Test
- void getSharesReturnsAllShares() {
- Portfolio portfolio = new Portfolio();
+ void removingNullShareThrowsException() {
+ assertThrows(IllegalArgumentException.class,
+ () -> testPortfolio.removeShare(null));
+ }
- Stock stock = new Stock("AAPL", "Apple", new BigDecimal("150"));
- Share share = new Share(stock, new BigDecimal("3"), new BigDecimal("150"));
+ @Test
+ void removingMoreSharesThanOwnedThrowsException() {
+ testPortfolio.addShare(testShare);
+ Share testShare2 = new Share(testStock, new BigDecimal("4"), testStock.getSalesPrice());
+ assertThrows(IllegalArgumentException.class,
+ () -> testPortfolio.removeShare(testShare2)
+ );
+ }
- portfolio.addShare(share);
+ @Test
+ void getSharesReturnsAllShares() {
+ testPortfolio.addShare(testShare);
- List shares = portfolio.getShares();
+ List shares = testPortfolio.getShares();
assertEquals(1, shares.size());
- assertTrue(shares.contains(share));
+ assertTrue(shares.contains(testShare));
}
@Test
void getSharesWithSymbolReturnsMatchingShares() {
- Portfolio portfolio = new Portfolio();
+ Stock testStock2 = new Stock("TSLA", "Tesla", new BigDecimal("200"));
- Stock apple = new Stock("AAPL", "Apple", new BigDecimal("150"));
- Stock tesla = new Stock("TSLA", "Tesla", new BigDecimal("200"));
-
- Share appleShare = new Share(apple,
- new BigDecimal("1"),
- new BigDecimal("150"));
- Share teslaShare = new Share(tesla,
+ Share testShare2 = new Share(testStock2,
new BigDecimal("1"),
new BigDecimal("200"));
- portfolio.addShare(appleShare);
- portfolio.addShare(teslaShare);
+ testPortfolio.addShare(testShare);
+ testPortfolio.addShare(testShare2);
- List result = portfolio.getShares("AAPL");
+ List result = testPortfolio.getShares("AAPL");
assertEquals(1, result.size());
- assertTrue(result.contains(appleShare));
+ assertTrue(result.contains(testShare));
}
@Test
- void containsReturnsFalseWhenShareNotPresent() {
- Portfolio portfolio = new Portfolio();
-
- Stock stock = new Stock("NVDA", "Nvidia", new BigDecimal("800"));
- Share share = new Share(stock, new BigDecimal("1"), new BigDecimal("800"));
-
- assertFalse(portfolio.contains(share));
+ void getSharesWithSymbolThrowsExceptionOnIllegalArgument() {
+ assertThrows(IllegalArgumentException.class,
+ () -> testPortfolio.getShares(null)
+ );
}
@Test
- void getNetWorthReturnsNetWorth() {
- Portfolio portfolio = new Portfolio();
-
- Stock stock = new Stock("NVDA", "Nvidia", new BigDecimal("800"));
- Share share = new Share(stock, new BigDecimal("1"), new BigDecimal("800"));
- portfolio.addShare(share);
-
- SaleCalculator saleCalculator = new SaleCalculator(share);
-
- BigDecimal calculatedNetWorth = saleCalculator.calculateTotal();
+ void containsReturnsFalseWhenShareNotPresent() {
+ assertFalse(testPortfolio.contains(testShare));
+ }
- BigDecimal actualNetWorth = portfolio.getNetWorth();
+ @Test
+ void containsThrowsExceptionOnIllegalArgument() {
+ assertThrows(IllegalArgumentException.class,
+ () -> testPortfolio.contains(null)
+ );
+ }
- assertEquals(calculatedNetWorth, actualNetWorth);
+ @Test
+ void getTotalSharesBySymbolReturnsCorrectValues() {
+ assertEquals(0,
+ BigDecimal.ZERO.compareTo(
+ testPortfolio.getTotalShareQuantityBySymbol("AAPL")
+ )
+ );
+
+ assertEquals(0,
+ BigDecimal.ZERO.compareTo(
+ testPortfolio.getTotalShareQuantityBySymbol(null)
+ )
+ );
+
+ testPortfolio.addShare(testShare);
+
+ assertEquals(0,
+ new BigDecimal("2.0").compareTo(
+ testPortfolio.getTotalShareQuantityBySymbol("AAPL")
+ )
+ );
}
}
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PurchaseTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PurchaseTest.java
index 42caf80..b2c03c6 100644
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PurchaseTest.java
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PurchaseTest.java
@@ -1,11 +1,14 @@
package edu.ntnu.idi.idatt2003.g40.mappe.model;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import java.math.BigDecimal;
-
import edu.ntnu.idi.idatt2003.g40.mappe.service.PurchaseCalculator;
+import java.math.BigDecimal;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
@@ -14,44 +17,70 @@
final class PurchaseTest {
/**
- * Valid test stock.
+ * Test purchase used in various tests.
* */
- private final Stock testStock =
- new Stock("AAPL", "Apple Inc.", new BigDecimal("100.00"));
+ private Purchase testPurchase;
/**
* Valid test share.
* */
- private final Share testShare =
- new Share(testStock, new BigDecimal("10"), new BigDecimal("10"));
+ private Share testShare;
/**
* Valid test purchase calculator.
* */
- private final PurchaseCalculator testPurchaseCalculator =
- new PurchaseCalculator(testShare);
+ private PurchaseCalculator testPurchaseCalculator;
/**
* Valid test player.
* */
- private final Player testPlayer =
- new Player("TestName", new BigDecimal("1000.00"));
+ private Player testPlayer;
+
+ @BeforeEach
+ void setUp() {
+ Stock testStock = new Stock(
+ "AAPL",
+ "Apple Inc.",
+ new BigDecimal("100.00")
+ );
+ testShare = new Share(
+ testStock,
+ new BigDecimal("10"),
+ new BigDecimal("10")
+ );
+ testPurchaseCalculator = new PurchaseCalculator(testShare);
+ testPlayer = new Player("TestName", new BigDecimal("1000.00"));
+ testPurchase = new Purchase(testShare, 1, testPurchaseCalculator);
+ }
@Test
void constructorSetsValues() {
- Purchase purchase = new Purchase(testShare, 1, testPurchaseCalculator);
-
- assertEquals(testShare, purchase.getShare());
- assertEquals(1, purchase.getWeek());
- assertEquals(testPurchaseCalculator, purchase.getCalculator());
+ assertEquals(testShare, testPurchase.getShare());
+ assertEquals(1, testPurchase.getWeek());
+ assertEquals(testPurchaseCalculator, testPurchase.getCalculator());
}
@Test
- void commitMethodSetsCommitToTrue() {
- Purchase purchase = new Purchase(testShare, 1, testPurchaseCalculator);
+ void constructorThrowsExceptionOnIllegalArguments() {
+ assertDoesNotThrow(
+ () -> new Purchase(testShare, 1, testPurchaseCalculator)
+ );
- purchase.commit(testPlayer);
+ assertThrows(IllegalArgumentException.class,
+ () -> new Purchase(null, 1, testPurchaseCalculator)
+ );
+ assertThrows(IllegalArgumentException.class,
+ () -> new Purchase(testShare, 0, testPurchaseCalculator)
+ );
+ assertThrows(IllegalArgumentException.class,
+ () -> new Purchase(testShare, 1, null)
+ );
+ }
- assertTrue(purchase.isCommited());
+ @Test
+ void commitMethodSetsCommitToTrue() {
+ assertFalse(testPurchase.isCommited());
+ testPurchase.commit(testPlayer);
+ assertTrue(testPurchase.isCommited());
}
}
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/SaleTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/SaleTest.java
index 22e4e4e..feda7ac 100644
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/SaleTest.java
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/SaleTest.java
@@ -1,11 +1,14 @@
package edu.ntnu.idi.idatt2003.g40.mappe.model;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import edu.ntnu.idi.idatt2003.g40.mappe.service.SaleCalculator;
import java.math.BigDecimal;
-
-import edu.ntnu.idi.idatt2003.g40.mappe.service.PurchaseCalculator;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
@@ -14,44 +17,70 @@
final class SaleTest {
/**
- * Valid test stock.
+ * Test sale used in various tests.
* */
- private final Stock testStock =
- new Stock("AAPL", "Apple Inc.", new BigDecimal("100.00"));
+ private Sale testSale;
/**
* Valid test share.
* */
- private final Share testShare =
- new Share(testStock, new BigDecimal("10"), new BigDecimal("10"));
+ private Share testShare;
/**
* Valid test purchase calculator.
* */
- private final PurchaseCalculator testSaleCalculator =
- new PurchaseCalculator(testShare);
+ private SaleCalculator testSaleCalculator;
/**
* Valid test player.
* */
- private final Player testPlayer =
- new Player("TestName", new BigDecimal("1000.00"));
+ private Player testPlayer;
+
+ @BeforeEach
+ void setUp() {
+ Stock testStock = new Stock(
+ "AAPL",
+ "Apple Inc.",
+ new BigDecimal("100.00")
+ );
+ testShare = new Share(
+ testStock,
+ new BigDecimal("10"),
+ new BigDecimal("10")
+ );
+ testSaleCalculator = new SaleCalculator(testShare);
+ testPlayer = new Player("TestName", new BigDecimal("1000.00"));
+ testSale = new Sale(testShare, 1, testSaleCalculator);
+ }
@Test
void constructorSetsValues() {
- Sale sale = new Sale(testShare, 1, testSaleCalculator);
-
- assertEquals(testShare, sale.getShare());
- assertEquals(1, sale.getWeek());
- assertEquals(testSaleCalculator, sale.getCalculator());
+ assertEquals(testShare, testSale.getShare());
+ assertEquals(1, testSale.getWeek());
+ assertEquals(testSaleCalculator, testSale.getCalculator());
}
@Test
- void commitMethodSetsCommitToTrue() {
- Sale sale = new Sale(testShare, 1, testSaleCalculator);
+ void constructorThrowsExceptionOnIllegalArguments() {
+ assertDoesNotThrow(
+ () -> new Purchase(testShare, 1, testSaleCalculator)
+ );
- sale.commit(testPlayer);
+ assertThrows(IllegalArgumentException.class,
+ () -> new Purchase(null, 1, testSaleCalculator)
+ );
+ assertThrows(IllegalArgumentException.class,
+ () -> new Purchase(testShare, 0, testSaleCalculator)
+ );
+ assertThrows(IllegalArgumentException.class,
+ () -> new Purchase(testShare, 1, null)
+ );
+ }
- assertTrue(sale.isCommited());
+ @Test
+ void commitMethodSetsCommitToTrue() {
+ assertFalse(testSale.isCommited());
+ testSale.commit(testPlayer);
+ assertTrue(testSale.isCommited());
}
}
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/SaveGameTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/SaveGameTest.java
new file mode 100644
index 0000000..907431e
--- /dev/null
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/SaveGameTest.java
@@ -0,0 +1,53 @@
+package edu.ntnu.idi.idatt2003.g40.mappe.model;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Test class for {@link SaveGame}.
+ * */
+class SaveGameTest {
+
+ /**
+ * {@link SaveGame} object to use for testing.
+ * */
+ private SaveGame testSaveGame;
+
+ @BeforeEach
+ void setUp() {
+ testSaveGame = new SaveGame("Save 1", 10, 100, "Stock path");
+ }
+
+ @Test
+ void constructorSetsValuesAsExpected() {
+ Assertions.assertEquals("Save 1", testSaveGame.getName());
+ Assertions.assertEquals(10, testSaveGame.getBalance());
+ Assertions.assertEquals(100, testSaveGame.getStartingCapital());
+ Assertions.assertEquals("Stock path", testSaveGame.getStockDataPath());
+ }
+
+ @Test
+ void constructorThrowsExceptionOnIllegalArguments() {
+ assertDoesNotThrow(
+ () -> new SaveGame("Save 2", 10, 100, "Stock path 2")
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new SaveGame("", 10, 100, "Stock path 2")
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new SaveGame("Save 2", 0, 100, "Stock path 2")
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new SaveGame("Save 2", 10, -10, "Stock path 2")
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/ShareTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/ShareTest.java
index bb78ea1..f73a2e4 100644
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/ShareTest.java
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/ShareTest.java
@@ -1,52 +1,114 @@
package edu.ntnu.idi.idatt2003.g40.mappe.model;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import java.math.BigDecimal;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
final class ShareTest {
- @Test
- void constructorStoresAllValuesCorrectly() {
- Stock stock = new Stock("AAPL", "Apple Inc.", new BigDecimal("150.00"));
+ /**
+ * Share to use for testing.
+ */
+ private Share testShare;
+
+ /**
+ * Stock to use for testing.
+ * */
+ private Stock testStock;
+
+ @BeforeEach
+ void setUp() {
+ testStock = new Stock(
+ "AAPL",
+ "Apple Inc.",
+ new BigDecimal("150.00")
+ );
BigDecimal quantity = new BigDecimal("10");
BigDecimal purchasePrice = new BigDecimal("145.50");
+ testShare = new Share(testStock, quantity, purchasePrice);
+ }
+
+ @Test
+ void constructorStoresAllValuesCorrectly() {
+ assertSame(testStock, testShare.getStock());
+
+ assertEquals(0,
+ new BigDecimal("10")
+ .compareTo(testShare.getQuantity())
+ );
+
+ assertEquals(0,
+ new BigDecimal("145.50")
+ .compareTo(testShare.getPurchasePrice())
+ );
+ }
+
+ @Test
+ void constructorThrowsExceptionOnIllegalArguments() {
+ assertDoesNotThrow(
+ () -> new Share(testStock, new BigDecimal("1"),
+ new BigDecimal("100"))
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new Share(null, new BigDecimal("1"), new BigDecimal("100"))
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new Share(testStock, null, new BigDecimal("100"))
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new Share(testStock, new BigDecimal("1"), null)
+ );
- Share share = new Share(stock, quantity, purchasePrice);
+ assertThrows(IllegalArgumentException.class,
+ () -> new Share(testStock, new BigDecimal("0"),
+ new BigDecimal("100"))
+ );
- assertSame(stock, share.getStock());
- assertEquals(quantity, share.getQuantity());
- assertEquals(purchasePrice, share.getPurchasePrice());
+ assertThrows(IllegalArgumentException.class,
+ () -> new Share(testStock, new BigDecimal("1"), new BigDecimal("0"))
+ );
}
@Test
void shareSupportsDecimalValues() {
- Stock stock = new Stock("TSLA", "Tesla Inc.", new BigDecimal("200.00"));
- Share share = new Share(
- stock,
+ Share testShare2 = new Share(
+ testStock,
new BigDecimal("2.5"),
new BigDecimal("198.75")
);
- assertEquals(new BigDecimal("2.5"), share.getQuantity());
- assertEquals(new BigDecimal("198.75"), share.getPurchasePrice());
+ assertEquals(0,
+ new BigDecimal("2.5")
+ .compareTo(testShare2.getQuantity())
+ );
+
+ assertEquals(0,
+ new BigDecimal("198.75")
+ .compareTo(testShare2.getPurchasePrice())
+ );
}
@Test
void getStockReturnsCorrectStockObject() {
- Stock stock = new Stock("NVDA",
- "NVIDIA Corporation",
- new BigDecimal("875.40"));
- Share share = new Share(stock,
- new BigDecimal("4"),
- new BigDecimal("850.00"));
-
- assertSame(stock, share.getStock());
- assertEquals("NVDA", share.getStock().getSymbol());
- assertEquals("NVIDIA Corporation", share.getStock().getCompany());
+ assertSame(testStock, testShare.getStock());
+ assertEquals(
+ "AAPL",
+ testShare.getStock().getSymbol()
+ );
+
+ assertEquals(
+ "Apple Inc.",
+ testShare.getStock().getCompany()
+ );
}
}
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/StockTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/StockTest.java
index c5f907d..8c7f48d 100644
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/StockTest.java
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/StockTest.java
@@ -1,15 +1,20 @@
package edu.ntnu.idi.idatt2003.g40.mappe.model;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
import java.math.BigDecimal;
import java.util.List;
-
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.*;
-
final class StockTest {
+ /**
+ * Stock to use for testing.
+ * */
private Stock testStock;
@BeforeEach
@@ -23,6 +28,28 @@ void constructorSetsSymbolAndCompany() {
assertEquals("Apple Inc.", testStock.getCompany());
}
+ @Test
+ void constructorThrowsExceptionOnIllegalArguments() {
+ assertDoesNotThrow(
+ () -> new Stock("AAPL", "APPLE INC.", new BigDecimal("100"))
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new Stock("", "APPLE INC.", new BigDecimal("100"))
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new Stock("AAPL", "", new BigDecimal("100"))
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new Stock("AAPL", "APPLE INC.", new BigDecimal("0"))
+ );
+ assertThrows(IllegalArgumentException.class,
+ () -> new Stock("AAPL", "APPLE INC.", null)
+ );
+ }
+
@Test
void addNewSalesPriceThenGetSalesPriceReturnsLastAddedPrice() {
testStock.addNewSalesPrice(new BigDecimal("123.45"));
@@ -39,11 +66,18 @@ void addNewSalesPriceTwiceGetSalesPriceReturnsMostRecent() {
}
@Test
- void addNewSalesPriceDoesNotAllowNullCurrentImplementation() {
-
+ void addNewSalesPriceThrowsExceptionOnIllegalArguments() {
assertThrows(IllegalArgumentException.class, () -> {
testStock.addNewSalesPrice(null);
});
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ testStock.addNewSalesPrice(new BigDecimal("0"));
+ });
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ testStock.addNewSalesPrice(new BigDecimal("-10"));
+ });
}
@Test
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/FileConverterTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/FileConverterTest.java
deleted file mode 100644
index 5cb5444..0000000
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/FileConverterTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package edu.ntnu.idi.idatt2003.g40.mappe.service;
-
-import edu.ntnu.idi.idatt2003.g40.mappe.model.Stock;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-class FileConverterTest {
-
- private FileConverter converter;
-
- private String validStockAsString1;
- private String validStockAsString2;
- private String validStockAsString3;
- private ArrayList allStocks;
- private String invalidStockAsString1;
-
- @BeforeEach
- void setUp() {
- validStockAsString1 = "AAPL, Apple inc., 251.42";
- validStockAsString2 = "NVID, Nvidia corp., 100.25";
- validStockAsString3 = "SAMS, Samsung corporation, 103.21";
-
- invalidStockAsString1 = "INVALID, This stock has an invalid code!, 100.21";
-
- allStocks = new ArrayList<>();
-
- allStocks.add(validStockAsString1);
- allStocks.add(validStockAsString2);
- allStocks.add(validStockAsString3);
- allStocks.add(invalidStockAsString1);
-
- converter = new FileConverter();
- }
-
- @Test
- void converter_returns_valid_stock_apple() {
-
- boolean stockIncluded = false;
-
- List stocksFromConverter = converter.getStocksFromStrings(allStocks);
-
- for (Stock s : stocksFromConverter) {
- if (s.getSymbol().equals("AAPL")) {
- stockIncluded = true;
- break;
- }
- }
-
- Assertions.assertTrue(stockIncluded);
- }
-
- @Test
- void converter_ignores_invalid_stock_representation() {
-
- boolean stockIncluded = false;
-
- List stocksFromConverter = converter.getStocksFromStrings(allStocks);
-
- for (Stock s : stocksFromConverter) {
- if (s.getSymbol().equals("INVALID")) {
- stockIncluded = true;
- break;
- }
- }
-
- Assertions.assertFalse(stockIncluded);
- }
-}
\ No newline at end of file
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/FileParserTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/FileParserTest.java
deleted file mode 100644
index 7d87b1a..0000000
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/FileParserTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package edu.ntnu.idi.idatt2003.g40.mappe.service;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class FileParserTest {
-
- private final String testStockDataPath = "/testStockData.txt";
-
- private final String absoluteTestStockDataPath = "src/main/resources/testStockData.txt";
- FileParser fileParser;
-
- private final String validStockFromFile = "NVID, Nvidida Corporation, 241.591";
-
- private final String invalidStockFromFile = "COOLI, This is a cool name, 252.2";
-
- private final String commentFromFile = "#Above me are some valid formats.";
-
- private List allLines = new ArrayList<>();
-
- private List validStocks = new ArrayList<>();
-
- @BeforeEach
- void setUp() throws Exception {
- fileParser = new FileParser(testStockDataPath);
- Path path = Paths.get(absoluteTestStockDataPath);
- allLines = Files.readAllLines(path);
- try {
- validStocks = fileParser.readFile();
- } catch (Exception _) {
- throw new Exception("Test failed");
- }
- }
-
- @Test
- void parser_gets_valid_stock_from_file() {
- assertTrue(allLines.contains(validStockFromFile));
- assertTrue(validStocks.contains(validStockFromFile));
- }
-
- @Test
- void parser_skips_comments_from_file() {
- assertTrue(allLines.contains(commentFromFile));
- assertFalse(validStocks.contains(commentFromFile));
- }
-
- @Test
- void parser_skips_invalid_stock_from_file() {
- assertTrue(allLines.contains(invalidStockFromFile));
- assertFalse(validStocks.contains(invalidStockFromFile));
- }
-}
\ No newline at end of file
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileManagerTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileManagerTest.java
new file mode 100644
index 0000000..74f0470
--- /dev/null
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileManagerTest.java
@@ -0,0 +1,56 @@
+package edu.ntnu.idi.idatt2003.g40.mappe.service;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+class StockFileManagerTest {
+
+ /**
+ * Path used to test file system.
+ * */
+ @TempDir
+ private Path tempDir;
+
+ @Test
+ void readFileParsesValidStocksAndFiltersOutCommentsAndInvalidData()
+ throws IOException {
+ Path tempFile = tempDir.resolve("testStockData.txt");
+ List mockFileData = List.of(
+ "# This is a comment header line and should be skipped",
+ "NVID, Nvidida Corporation, 241.591",
+ "AAPL, Apple Inc, 175.50",
+ "",
+ "INVALID_ROW_MISSING_COLUMNS",
+ "COOLI, This is a cool name but missing price token"
+ );
+ Files.write(tempFile, mockFileData);
+
+ StockFileManager stockFileManager =
+ new StockFileManager(tempFile.toString());
+ List parsingResults = stockFileManager.readFile();
+
+ assertEquals(2, parsingResults.size());
+ assertTrue(parsingResults.contains("NVID, Nvidida Corporation, 241.591"));
+ assertTrue(parsingResults.contains("AAPL, Apple Inc, 175.50"));
+ assertFalse(parsingResults.stream().anyMatch(line -> line.startsWith("#")));
+ }
+
+ @Test
+ void readFileHandlesEmptyFileGracefully() throws IOException {
+ Path emptyFile = tempDir.resolve("emptyStockData.txt");
+ Files.createFile(emptyFile);
+
+ StockFileManager stockFileManager = new StockFileManager(emptyFile.toString());
+ List results = stockFileManager.readFile();
+
+ assertTrue(results.isEmpty());
+ }
+}
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileParserTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileParserTest.java
new file mode 100644
index 0000000..8fe5158
--- /dev/null
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/StockFileParserTest.java
@@ -0,0 +1,105 @@
+package edu.ntnu.idi.idatt2003.g40.mappe.service;
+
+import edu.ntnu.idi.idatt2003.g40.mappe.model.Stock;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class StockFileParserTest {
+
+ /**
+ * Test stock parser to use.
+ * */
+ private StockFileParser testParser;
+
+ /**
+ * List of strings containing valid and invalid representations
+ * of stocks.
+ * */
+ private ArrayList allStocks;
+
+ @BeforeEach
+ void setUp() {
+ String validStockAsString1 = "AAPL, Apple inc., 251.42";
+ String validStockAsString2 = "NVID, Nvidia corp., 100.25";
+ String validStockAsString3 = "SAMS, Samsung corporation, 103.21";
+
+ String invalidStockAsString1 = "INVALID, This stock has an invalid code!, 100.21";
+
+ allStocks = new ArrayList<>();
+
+ allStocks.add(validStockAsString1);
+ allStocks.add(validStockAsString2);
+ allStocks.add(validStockAsString3);
+ allStocks.add(invalidStockAsString1);
+
+ testParser = new StockFileParser();
+ }
+
+ @Test
+ void getStocksFromStringsReturnsValidStocks() {
+
+ boolean stockIncluded = false;
+
+ List stocksFromConverter = testParser.getStocksFromStrings(allStocks);
+
+ for (Stock s : stocksFromConverter) {
+ if (s.getSymbol().equals("AAPL")) {
+ stockIncluded = true;
+ break;
+ }
+ }
+
+ Assertions.assertTrue(stockIncluded);
+ }
+
+ @Test
+ void getStocksFromStringsIgnoresInvalidStocks() {
+
+ boolean stockIncluded = false;
+
+ List stocksFromConverter = testParser.getStocksFromStrings(allStocks);
+
+ for (Stock s : stocksFromConverter) {
+ if (s.getSymbol().equals("INVALID")) {
+ stockIncluded = true;
+ break;
+ }
+ }
+
+ Assertions.assertFalse(stockIncluded);
+ }
+
+ @Test
+ void stocksToStringsConvertsValidStocksToCsvFormat() {
+ Stock apple = new Stock("AAPL", "Apple Inc", new BigDecimal("175.50"));
+ Stock tesla = new Stock("TSLA", "Tesla Inc", new BigDecimal("200.00"));
+ List stocks = List.of(apple, tesla);
+
+ List result = testParser.stocksToStrings(stocks);
+
+ assertEquals(2, result.size());
+ assertEquals("AAPL, Apple Inc, 175.50", result.get(0));
+ assertEquals("TSLA, Tesla Inc, 200.00", result.get(1));
+ }
+
+ @Test
+ void stocksToStringsThrowsExceptionOnNullOrEmptyList() {
+ List emptyList = new ArrayList<>();
+
+ assertThrows(IllegalArgumentException.class,
+ () -> testParser.stocksToStrings(null)
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> testParser.stocksToStrings(emptyList)
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/TransactionFactoryTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/TransactionFactoryTest.java
index 6b25c35..1f49c32 100644
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/TransactionFactoryTest.java
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/TransactionFactoryTest.java
@@ -1,7 +1,9 @@
package edu.ntnu.idi.idatt2003.g40.mappe.service;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Purchase;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Sale;
@@ -15,56 +17,45 @@
class TransactionFactoryTest {
private Stock testStock;
private Share testShare;
- private Sale testSale;
- private Purchase testPurchase;
- private SaleCalculator testSaleCalculator;
- private PurchaseCalculator testPurchaseCalculator;
@BeforeEach
void setUp() {
testStock = new Stock("AAPL", "APPLE INC.", new BigDecimal("100.00"));
testShare = new Share(testStock, new BigDecimal("10.0"), testStock.getSalesPrice());
- testSaleCalculator = new SaleCalculator(testShare);
- testSale = new Sale(testShare, 1, testSaleCalculator);
- testPurchaseCalculator = new PurchaseCalculator(testShare);
- testPurchase = new Purchase(testShare, 1, testPurchaseCalculator);
}
@Test
void factoryReturnsCorrectSale() {
- Transaction sale2 = TransactionFactory.createTransaction(TransactionType.SALE, testShare, 1, testSaleCalculator);
- assertTrue(equalTransactions(testSale, sale2));
+ int targetWeek = 1;
+ Transaction transaction = TransactionFactory.createTransaction(TransactionType.SALE, testShare, targetWeek);
+ assertNotNull(transaction);
+ assertInstanceOf(Sale.class, transaction);
+ assertEquals(targetWeek, transaction.getWeek());
+ assertEquals(testShare, transaction.getShare());
+ assertNotNull(transaction.getCalculator());
}
@Test
void factoryReturnsCorrectPurchase() {
- Transaction purchase2 = TransactionFactory.createTransaction(TransactionType.PURCHASE, testShare, 1, testPurchaseCalculator);
- assertTrue(equalTransactions(testPurchase, purchase2));
+ int targetWeek = 1;
+ Transaction transaction = TransactionFactory.createTransaction(TransactionType.PURCHASE, testShare, targetWeek);
+ assertNotNull(transaction);
+ assertInstanceOf(Purchase.class, transaction);
+ assertEquals(targetWeek, transaction.getWeek());
+ assertEquals(testShare, transaction.getShare());
+ assertNotNull(transaction.getCalculator());
}
@Test
void factoryThrowsErrors() {
- assertThrows(IllegalArgumentException.class, () -> {
- TransactionFactory.createTransaction(TransactionType.PURCHASE, null, 1, testPurchaseCalculator);
- });
-
- assertThrows(IllegalArgumentException.class, () -> {
- TransactionFactory.createTransaction(TransactionType.PURCHASE, testShare, -1, testPurchaseCalculator);
- });
-
- assertThrows(IllegalArgumentException.class, () -> {
- TransactionFactory.createTransaction(TransactionType.PURCHASE, testShare, 1, null);
- });
-
- assertThrows(IllegalArgumentException.class, () -> {
- TransactionFactory.createTransaction(null, testShare, 1, testPurchaseCalculator);
- });
- }
-
- private boolean equalTransactions(final Transaction transaction1, final Transaction transaction2) {
- return (transaction1.getWeek() == transaction2.getWeek()
- && transaction1.getShare() == transaction2.getShare()
- && transaction1.getCalculator() == transaction2.getCalculator()
- && transaction1.isCommited() == transaction2.isCommited());
+ assertThrows(IllegalArgumentException.class,
+ () -> TransactionFactory.createTransaction(TransactionType.PURCHASE, null, 1)
+ );
+ assertThrows(IllegalArgumentException.class,
+ () -> TransactionFactory.createTransaction(TransactionType.PURCHASE, testShare, -1)
+ );
+ assertThrows(IllegalArgumentException.class,
+ () -> TransactionFactory.createTransaction(null, testShare, 1)
+ );
}
}
\ No newline at end of file
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventManagerTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventManagerTest.java
index 716e96e..eee20c2 100644
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventManagerTest.java
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventManagerTest.java
@@ -1,99 +1,188 @@
package edu.ntnu.idi.idatt2003.g40.mappe.service.event;
-import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewData;
-import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewEnum;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.*;
-
class EventManagerTest {
private enum TestEventTypes implements EventChannel {
+ /**
+ * Test event type 1.
+ * */
TEST_TYPE_1,
+
+ /**
+ * Test event type 2.
+ * */
TEST_TYPE_2,
+
+ /**
+ * Test event type 3 (Used by both subscribers).
+ * */
TEST_TYPE_3;
- @Override
- public String getName() {
- return this.name();
- }
}
- private GenericEventPublisher testEventPublisher;
- private GenericEventPublisher testEventPublisher2;
- private GenericEventPublisher testEventPublisher3;
+ /**
+ * Event manager used for testing.
+ * */
+ private EventManager testEventManager;
- private GenericEventSubscriber testEventSubscriber;
- private GenericEventSubscriber testEventSubscriber2;
+ /**
+ * Example event subscriber 1.
+ * */
+ private SampleEventSubscriber sampleEventSubscriber1;
- private EventManager testEventManager;
+ /**
+ * Example event subscriber 2.
+ * */
+ private SampleEventSubscriber sampleEventSubscriber2;
@BeforeEach
void setUp() {
testEventManager = new EventManager();
- testEventSubscriber = new GenericEventSubscriber();
- testEventSubscriber2 = new GenericEventSubscriber();
+ sampleEventSubscriber1 = new SampleEventSubscriber();
+ sampleEventSubscriber2 = new SampleEventSubscriber();
+
+ testEventManager.addSubscriber(sampleEventSubscriber1, TestEventTypes.TEST_TYPE_1);
+ testEventManager.addSubscriber(sampleEventSubscriber1, TestEventTypes.TEST_TYPE_3);
- testEventPublisher = new GenericEventPublisher(testEventManager, TestEventTypes.TEST_TYPE_1);
- testEventPublisher2 = new GenericEventPublisher(testEventManager, TestEventTypes.TEST_TYPE_2);
- testEventPublisher3 = new GenericEventPublisher(testEventManager, TestEventTypes.TEST_TYPE_3);
+ testEventManager.addSubscriber(sampleEventSubscriber2, TestEventTypes.TEST_TYPE_2);
+ testEventManager.addSubscriber(sampleEventSubscriber2, TestEventTypes.TEST_TYPE_3);
+ }
+
+ @Test
+ void addingSubscriberNullParametersThrowsException() {
+ assertThrows(IllegalArgumentException.class,
+ () -> testEventManager.addSubscriber(
+ null,
+ TestEventTypes.TEST_TYPE_2));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> testEventManager.addSubscriber(
+ sampleEventSubscriber1,
+ null));
+
+ // Will only throw exception if sample event subscriber 1 is duplicate.
+ assertDoesNotThrow(
+ () -> testEventManager.addSubscriber(
+ sampleEventSubscriber1,
+ TestEventTypes.TEST_TYPE_2));
- testEventManager.addSubscriber(testEventSubscriber, TestEventTypes.TEST_TYPE_1);
- testEventManager.addSubscriber(testEventSubscriber2, TestEventTypes.TEST_TYPE_2);
+ }
+
+ @Test
+ void addingDuplicateSubscriberForSameChannelThrowsException() {
+ assertDoesNotThrow(
+ () -> testEventManager.addSubscriber(
+ sampleEventSubscriber1,
+ TestEventTypes.TEST_TYPE_2));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> testEventManager.addSubscriber(
+ sampleEventSubscriber1,
+ TestEventTypes.TEST_TYPE_1));
}
@Test
void firedEventCaughtByCorrectSubscriber() {
- assertFalse(testEventSubscriber.invokedEvent);
- testEventPublisher.fireEvent();
- assertTrue(testEventSubscriber.invokedEvent);
+ String dataToSend = "Data for type 1";
+
+ assertFalse(sampleEventSubscriber1.getInvoked());
+ assertNull(sampleEventSubscriber1.getLastReceivedData());
+
+ EventData eventData = new EventData<>(
+ TestEventTypes.TEST_TYPE_1,
+ dataToSend
+ );
+ testEventManager.invokeEvent(eventData);
+
+ assertTrue(sampleEventSubscriber1.getInvoked());
+ assertEquals(dataToSend, sampleEventSubscriber1.getLastReceivedData());
}
@Test
void firedEventNotCaughtByIncorrectSubscriber() {
- assertFalse(testEventSubscriber.invokedEvent);
- testEventPublisher2.fireEvent();
- assertFalse(testEventSubscriber.invokedEvent);
+ String dataToSend = "Data for type 1";
+
+ assertFalse(sampleEventSubscriber1.getInvoked());
+ assertNull(sampleEventSubscriber1.getLastReceivedData());
+ assertFalse(sampleEventSubscriber2.getInvoked());
+ assertNull(sampleEventSubscriber2.getLastReceivedData());
+
+ EventData eventData = new EventData<>(
+ TestEventTypes.TEST_TYPE_2,
+ dataToSend
+ );
+ testEventManager.invokeEvent(eventData);
+
+ assertFalse(sampleEventSubscriber1.getInvoked());
+ assertNull(sampleEventSubscriber1.getLastReceivedData());
+
+ assertTrue(sampleEventSubscriber2.getInvoked());
+ assertEquals(dataToSend, sampleEventSubscriber2.getLastReceivedData());
}
@Test
- void firedEventThrowsErrorWhenNoSubscribers() {
- assertFalse(testEventSubscriber.invokedEvent);
- assertThrows(IllegalArgumentException.class, () -> {
- testEventPublisher3.fireEvent();
- });
- assertFalse(testEventSubscriber.invokedEvent);
- }
+ void firedEventsWithMultipleSubscribersCaughtByAllSubscribers() {
+ String dataToSend = "Data for type 1";
- private class GenericEventPublisher implements EventPublisher {
+ assertFalse(sampleEventSubscriber1.getInvoked());
+ assertNull(sampleEventSubscriber1.getLastReceivedData());
+ assertFalse(sampleEventSubscriber2.getInvoked());
+ assertNull(sampleEventSubscriber2.getLastReceivedData());
- private final ViewData viewData;
- private final EventData eventData;
- private final EventManager eventManager;
+ EventData eventData = new EventData<>(
+ TestEventTypes.TEST_TYPE_3,
+ dataToSend
+ );
+ testEventManager.invokeEvent(eventData);
- public GenericEventPublisher(final EventManager eventManager, final TestEventTypes eventType) {
- viewData = new ViewData(ViewEnum.IN_GAME);
- eventData = new EventData(eventType, viewData);
- this.eventManager = eventManager;
- }
+ assertTrue(sampleEventSubscriber1.getInvoked());
+ assertEquals(dataToSend, sampleEventSubscriber1.getLastReceivedData());
- public void fireEvent() {
- invoke(eventData, eventManager);
- }
+ assertTrue(sampleEventSubscriber2.getInvoked());
+ assertEquals(dataToSend, sampleEventSubscriber2.getLastReceivedData());
+ }
- @Override
- public void invoke(EventData data, EventManager eventManager) {
- eventManager.invokeEvent(data);
- }
+ @Test
+ void firedEventThrowsErrorWhenDataIsNull() {
+
+ EventData invalidEventData = new EventData<>(
+ TestEventTypes.TEST_TYPE_1,
+ null
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> testEventManager.invokeEvent(null));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> testEventManager.invokeEvent(invalidEventData));
}
- private class GenericEventSubscriber implements EventSubscriber {
- public boolean invokedEvent = false;
+ private static class SampleEventSubscriber implements EventSubscriber {
+ private boolean invokedEvent = false;
+ private Object lastReceivedData = null;
@Override
- public void handleEvent(EventData data) {
+ public void handleEvent(final EventData data) {
invokedEvent = true;
+ lastReceivedData = data.data();
+ }
+
+ private boolean getInvoked() {
+ return invokedEvent;
+ }
+
+ private Object getLastReceivedData() {
+ return lastReceivedData;
}
}
}
\ No newline at end of file
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewControllerTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewControllerTest.java
index e105cdf..948d9ca 100644
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewControllerTest.java
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewControllerTest.java
@@ -11,30 +11,40 @@
import org.testfx.framework.junit5.ApplicationTest;
class ViewControllerTest extends ApplicationTest {
- private EventManager testEventManager;
- private GenericViewController testViewController;
+
+ /**
+ * View element instance used for testing.
+ * */
private GenericViewElement testViewElement;
@Override
- public void start(Stage stage) {
- testEventManager = new EventManager();
- testViewElement = new ViewControllerTest.GenericViewElement(new Pane());
- testViewController = new GenericViewController(testViewElement, testEventManager);
+ public void start(final Stage stage) {
+ EventManager testEventManager = new EventManager();
+ testViewElement = new GenericViewElement(new Pane());
+ new GenericViewController(testViewElement, testEventManager);
}
@Test
void controllerElementSetsButtonBehavior() {
- assertFalse(testViewElement.buttonPressed);
+ assertFalse(testViewElement.getButtonPressed());
testViewElement.getInteractableButton().fire();
- assertTrue(testViewElement.buttonPressed);
+ assertTrue(testViewElement.getButtonPressed());
}
private enum GenericViewActions {
+ /**
+ * Action used for testing purposes.
+ * */
TEST_ACTION;
}
- private class GenericViewElement extends ViewElement {
- public Boolean buttonPressed = false;
+ /**
+ * Test class meant for simulating a view element instance.
+ *
+ * @see ViewElement
+ * */
+ private static class GenericViewElement extends ViewElement {
+ private boolean buttonPressed = false;
private Button interactableButton;
protected GenericViewElement(final Pane rootPane) {
@@ -51,11 +61,22 @@ public Button getInteractableButton() {
return interactableButton;
}
+ public boolean getButtonPressed() {
+ return buttonPressed;
+ }
+
@Override
- protected void initStyling() { }
+ protected void initStyling() {
+ // Empty
+ }
}
- private class GenericViewController extends ViewController {
+ /**
+ * View controller class used for testing.
+ *
+ * @see ViewController
+ * */
+ private static class GenericViewController extends ViewController {
protected GenericViewController(final ViewControllerTest.GenericViewElement viewElement,
final EventManager eventManager)
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewElementTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewElementTest.java
new file mode 100644
index 0000000..ad4b19e
--- /dev/null
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewElementTest.java
@@ -0,0 +1,107 @@
+package edu.ntnu.idi.idatt2003.g40.mappe.view;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventManager;
+import javafx.scene.control.Button;
+import javafx.scene.layout.Pane;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+import org.junit.jupiter.api.Test;
+import org.testfx.framework.junit5.ApplicationTest;
+
+class ViewElementTest extends ApplicationTest {
+
+ /**
+ * View element instance used for testing.
+ * */
+ private ViewElementTest.GenericViewElement testViewElement;
+
+ /**
+ * Root of generic view instance.
+ * */
+ private Pane rootPane;
+
+ @Override
+ public void start(final Stage stage) {
+ rootPane = new Pane();
+ testViewElement = new ViewElementTest.GenericViewElement(rootPane);
+ }
+
+ @Test
+ void constructorSetsValuesAsExpected() {
+ assertEquals(rootPane, testViewElement.getRootPane());
+ }
+
+ @Test
+ void constructorThrowsExceptionWhenIllegalArguments() {
+ assertDoesNotThrow(
+ () -> new ViewElementTest.GenericViewElement(new VBox())
+ );
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new ViewElementTest.GenericViewElement(null)
+ );
+ }
+
+ @Test
+ void setOnActionThrowsExceptionOnIllegalArguments() {
+ assertThrows(IllegalArgumentException.class,
+ () -> testViewElement.setOnAction(
+ GenericViewActions.UNUSED_TEST_ACTION,
+ () -> testViewElement.setButtonPressed()
+ )
+ );
+ }
+
+ private enum GenericViewActions {
+ /**
+ * Action used for testing purposes.
+ * */
+ TEST_ACTION,
+
+ /**
+ * Unused test action to check exception throwing.
+ * */
+ UNUSED_TEST_ACTION
+ }
+
+ /**
+ * Test class meant for simulating a view element instance.
+ *
+ * @see ViewElement
+ * */
+ private static class GenericViewElement extends ViewElement {
+ private boolean buttonPressed = false;
+ private Button interactableButton;
+
+ protected GenericViewElement(final Pane rootPane) {
+ super(rootPane, ViewElementTest.GenericViewActions.class);
+ }
+
+ @Override
+ protected void initLayout() {
+ interactableButton = new Button("Click me!");
+ registerButton(ViewElementTest.GenericViewActions.TEST_ACTION, interactableButton);
+ }
+
+ public Button getInteractableButton() {
+ return interactableButton;
+ }
+
+ public boolean getButtonPressed() {
+ return buttonPressed;
+ }
+
+ public void setButtonPressed() {
+ buttonPressed = true;
+ }
+
+ @Override
+ protected void initStyling() {
+ // Empty
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewManagerTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewManagerTest.java
index f4d04e9..0ecc2a9 100644
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewManagerTest.java
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewManagerTest.java
@@ -2,7 +2,6 @@
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventManager;
import javafx.scene.Scene;
-import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import org.junit.jupiter.api.Assertions;
@@ -17,16 +16,22 @@
*/
class ViewManagerTest extends ApplicationTest {
+ /**
+ * View manager object used for testing.
+ * */
private ViewManager testViewManager;
- private EventManager testEventManager;
- private ViewManagerTest.GenericViewElement genericViewElement;
+
+ /**
+ * Generic view element instance used for testing.
+ * */
+ private ViewManagerTest.GenericViewElement testViewElement;
@Override
public void start(final Stage stage) {
stage.setScene(new Scene(new Pane()));
- testEventManager = new EventManager();
+ EventManager testEventManager = new EventManager();
testViewManager = new ViewManager(stage, testEventManager);
- genericViewElement = new GenericViewElement(new Pane(), ViewEnum.IN_GAME);
+ testViewElement = new GenericViewElement(new Pane(), ViewEnum.IN_GAME);
}
@Test
@@ -36,42 +41,59 @@ void testViewManagerHoldsNoViewAtStart() {
@Test
void addingMultipleOfSameViewThrowsException() {
- testViewManager.addView(genericViewElement);
+ testViewManager.addView(testViewElement);
Assertions.assertThrows(IllegalArgumentException.class, () -> {
- testViewManager.addView(genericViewElement);
+ testViewManager.addView(testViewElement);
});
}
@Test
void testViewManagerSettingCorrectView() {
- testViewManager.addView(genericViewElement);
- testViewManager.setScene(genericViewElement);
- Assertions.assertEquals(genericViewElement, testViewManager.getCurrentView());
+ testViewManager.addView(testViewElement);
+ testViewManager.setScene(testViewElement);
+ Assertions.assertEquals(testViewElement, testViewManager.getCurrentView());
}
@Test
void testViewManagerThrowingErrorWhenSettingNonExistentView() {
Assertions.assertThrows(IllegalArgumentException.class, () -> {
- testViewManager.setScene(genericViewElement);
+ testViewManager.setScene(testViewElement);
});
}
@Test
void throwsErrorWhenAddingWidgetWithNoViewName() {
- genericViewElement = new GenericViewElement(new Pane());
+ testViewElement = new GenericViewElement(new Pane());
Assertions.assertThrows(IllegalArgumentException.class, () -> {
- testViewManager.addView(genericViewElement);
+ testViewManager.addView(testViewElement);
});
}
- private enum GenericActions {
- ACTION_1;
+ @Test
+ void settingViewThroughViewDataWorksAsExpected() {
+ GenericViewElement testViewElement2 =
+ new GenericViewElement(new Pane(), ViewEnum.MAIN_MENU);
+
+ testViewManager.addView(testViewElement2);
+ ViewData viewData = new ViewData(ViewEnum.MAIN_MENU);
+
+ Assertions.assertNotEquals(testViewElement2,
+ testViewManager.getCurrentView());
+
+ testViewManager.setScene(viewData);
+ Assertions.assertEquals(testViewElement2, testViewManager.getCurrentView());
}
+ private enum GenericActions {
+ // Empty.
+ }
- private class GenericViewElement extends ViewElement {
- public Boolean buttonPressed = false;
- private Button interactableButton;
+ /**
+ * Generic view element class used to test view elements within
+ * the view manager.
+ * */
+ private static class GenericViewElement
+ extends ViewElement {
protected GenericViewElement(final Pane rootPane, final ViewEnum viewName) {
super(rootPane, viewName, GenericActions.class);
@@ -83,15 +105,12 @@ protected GenericViewElement(final Pane rootPane) {
@Override
protected void initLayout() {
- interactableButton = new Button("Click me!");
- registerButton(GenericActions.ACTION_1, interactableButton);
- }
-
- public Button getInteractableButton() {
- return interactableButton;
+ // Empty for view manager testing.
}
@Override
- protected void initStyling() { }
+ protected void initStyling() {
+ // Empty for view manager testing.
+ }
}
}