diff --git a/src/main/java/millions/Exchange.java b/src/main/java/millions/Exchange.java index 98584f2..57d077c 100644 --- a/src/main/java/millions/Exchange.java +++ b/src/main/java/millions/Exchange.java @@ -18,19 +18,33 @@ public Exchange(String name, List stockList) { this.stocks = new HashMap<>(); this.weekNumber = 1; + if (name == null || name.isBlank()) { + throw new IllegalArgumentException("Exchange name cannot be null or blank"); + } + // Populate the stocks map to get ticker -> stock for (Stock stock : stockList) { this.stocks.put(stock.getSymbol(), stock); } } - public void buy(Player player, Stock stock, BigDecimal quantity) { + public void buy(String symbol, Player player, BigDecimal quantity) { + + Stock stock = this.stocks.get(symbol); + if (stock == null) { + throw new IllegalArgumentException("Stock not found"); + } + Share shareToBuy = new Share(stock, quantity, stock.getSalesPrice()); Purchase purchase = new Purchase(shareToBuy, this.weekNumber); purchase.commit(player); } - public void sell(Player player, Share share) { + public void buy(String symbol, Player player, int quantity) { + this.buy(symbol, player, BigDecimal.valueOf(quantity)); + } + + public void sell(Share share, Player player) { Sale sale = new Sale(share, weekNumber); sale.commit(player); } diff --git a/src/main/java/millions/Player.java b/src/main/java/millions/Player.java index 747c8d0..6c01c2c 100644 --- a/src/main/java/millions/Player.java +++ b/src/main/java/millions/Player.java @@ -15,13 +15,27 @@ public Player(String name, BigDecimal startingMoney) { this.money = startingMoney; this.portfolio = new Portfolio(); this.transactionArchive = new TransactionArchive(); + + if (name == null || name.isBlank()) { + throw new IllegalArgumentException("Player name cannot be null or blank"); + } + + if (startingMoney == null || startingMoney.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("Starting money cannot be null or negative"); + } } public void addMoney(BigDecimal amount) { + if (amount == null || amount.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("Amount cannot be null or negative"); + } this.money = this.money.add(amount); } public void withdrawMoney(BigDecimal amount) { + if (amount == null || amount.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("Amount cannot be null or negative"); + } this.money = this.money.subtract(amount); } diff --git a/src/main/java/millions/Share.java b/src/main/java/millions/Share.java index 6eddada..5db6738 100644 --- a/src/main/java/millions/Share.java +++ b/src/main/java/millions/Share.java @@ -11,6 +11,20 @@ public Share(Stock stock, BigDecimal quantity, BigDecimal purchasePrice) { this.stock = stock; this.quantity = quantity; this.purchasePrice = purchasePrice; + + if (stock == null) { + throw new IllegalArgumentException("Stock cannot be null"); + } + if (quantity == null) { + throw new IllegalArgumentException("Quantity cannot be null"); + } + if (purchasePrice == null) { + throw new IllegalArgumentException("Purchase price cannot be null"); + } + } + + public Share(Stock stock, int quantity, BigDecimal purchasePrice) { + this(stock, BigDecimal.valueOf(quantity), purchasePrice); } public Stock getStock() { diff --git a/src/main/java/millions/Stock.java b/src/main/java/millions/Stock.java index 886ecba..632c4b7 100644 --- a/src/main/java/millions/Stock.java +++ b/src/main/java/millions/Stock.java @@ -1,6 +1,7 @@ package millions; import java.math.BigDecimal; +import java.util.ArrayList; import java.util.List; public class Stock { @@ -11,7 +12,19 @@ public class Stock { public Stock(String symbol, String company, List prices) { this.symbol = symbol; this.company = company; - this.prices = prices; + this.prices = new ArrayList<>(prices); + + if (symbol == null || symbol.isBlank()) { + throw new IllegalArgumentException("Symbol cannot be null or blank"); + } + + if (company == null || company.isBlank()) { + throw new IllegalArgumentException("Company cannot be null or blank"); + } + } + + public Stock(String symbol, String company, BigDecimal initialPrice) { + this(symbol, company, new ArrayList<>(List.of(initialPrice))); } public String getSymbol() { diff --git a/src/main/java/millions/calculators/SaleCalculator.java b/src/main/java/millions/calculators/SaleCalculator.java index 2bddf30..1ceaed6 100644 --- a/src/main/java/millions/calculators/SaleCalculator.java +++ b/src/main/java/millions/calculators/SaleCalculator.java @@ -12,6 +12,8 @@ public class SaleCalculator implements TransactionCalculator { public SaleCalculator(Share share) { super(); this.purchasePrice = share.getPurchasePrice(); + this.salesPrice = share.getStock().getSalesPrice(); + this.quantity = share.getQuantity(); } @Override diff --git a/src/test/java/millions/ExchangeTest.java b/src/test/java/millions/ExchangeTest.java index a852a0d..f0479b2 100644 --- a/src/test/java/millions/ExchangeTest.java +++ b/src/test/java/millions/ExchangeTest.java @@ -2,4 +2,82 @@ import static org.junit.jupiter.api.Assertions.*; -class ExchangeTest {} +import java.math.BigDecimal; +import java.util.List; +import org.junit.jupiter.api.Test; + +class ExchangeTest { + @Test + public void testGetters() { + Stock s1 = new Stock("PEAR", "Pear Inc.", BigDecimal.valueOf(300)); + Stock s2 = new Stock("DOGL", "DOOGLE Inc.", BigDecimal.valueOf(200.00)); + Stock s3 = new Stock("MSFT", "EpsteinSoft Inc.", BigDecimal.valueOf(0.02)); + + Exchange exchange = new Exchange("exchange", List.of(s1, s2)); + + assertFalse(exchange.hasStock("MSFT")); + assertTrue(exchange.getStock("DOGL").equals(s2)); + assertNull(exchange.getStock("XYZ")); + assertTrue(exchange.findStocks("Amozon").isEmpty()); + + assertTrue(exchange.hasStock("DOGL")); + assertFalse(exchange.findStocks("Pear").isEmpty()); + assertFalse(exchange.findStocks("PE").isEmpty()); + + assertTrue(exchange.findStocks("Inc").size() == 2); + } + + @Test + public void happyPath() { + Stock s1 = new Stock("PEAR", "Pear Inc.", BigDecimal.valueOf(300)); + Stock s2 = new Stock("DOGL", "DOOGLE Inc.", BigDecimal.valueOf(200.00)); + Stock s3 = new Stock("MSFT", "EpsteinSoft Inc.", BigDecimal.valueOf(0.02)); + + Exchange exchange = new Exchange("exchange", List.of(s1, s2, s3)); + Player player = new Player("name", BigDecimal.valueOf(1000)); + + BigDecimal previousMoney = player.getMoney(); + exchange.buy("PEAR", player, 2); + assertTrue(previousMoney.compareTo(player.getMoney()) > 0); + + BigDecimal previousPrice = s1.getSalesPrice(); + exchange.advance(); + assertFalse(previousPrice.equals(s1.getSalesPrice())); + + Share share = player.getPortfolio().getShares().getFirst(); + + previousMoney = player.getMoney(); + exchange.sell(share, player); + assertTrue(previousMoney.compareTo(player.getMoney()) < 0); + assertTrue(player.getPortfolio().getShares().isEmpty()); + } + + @Test + public void testNullsAndInvalid() { + Stock s1 = new Stock("MSFT", "EpsteinSoft Inc.", BigDecimal.valueOf(0.02)); + + Exchange exchange = new Exchange("exchange", List.of(s1)); + Player player = new Player("name", BigDecimal.valueOf(1000)); + + Share unownedShare = new Share(s1, BigDecimal.valueOf(1), s1.getSalesPrice()); + assertThrows(IllegalStateException.class, () -> exchange.sell(unownedShare, player)); + + Player noMoney = new Player("nomoney", BigDecimal.valueOf(0)); + assertThrows(IllegalStateException.class, () -> exchange.buy("MSFT", noMoney, 1)); + + assertThrows( + IllegalArgumentException.class, + () -> new Exchange("", List.of(new Stock("PEAR", "Pear Inc.", BigDecimal.valueOf(300))))); + + assertThrows( + IllegalArgumentException.class, + () -> new Exchange(null, List.of(new Stock("PEAR", "Pear Inc.", BigDecimal.valueOf(300))))); + + assertThrows(IllegalArgumentException.class, () -> exchange.buy("DOGL", player, 2)); + + assertThrows(IllegalArgumentException.class, () -> exchange.buy("DOGL", player, -2)); + + assertThrows( + IllegalArgumentException.class, () -> exchange.buy("DOGL", player, BigDecimal.valueOf(-2))); + } +} diff --git a/src/test/java/millions/PlayerTest.java b/src/test/java/millions/PlayerTest.java index 62a3263..99cfb45 100644 --- a/src/test/java/millions/PlayerTest.java +++ b/src/test/java/millions/PlayerTest.java @@ -2,4 +2,37 @@ import static org.junit.jupiter.api.Assertions.*; -class PlayerTest {} +import java.math.BigDecimal; +import org.junit.jupiter.api.Test; + +class PlayerTest { + + @Test + public void happyPath() { + Player player = new Player("name", BigDecimal.valueOf(1000)); + assertEquals(BigDecimal.valueOf(1000), player.getMoney()); + + player.addMoney(BigDecimal.valueOf(500)); + assertEquals(BigDecimal.valueOf(1500), player.getMoney()); + + player.withdrawMoney(BigDecimal.valueOf(200)); + assertEquals(BigDecimal.valueOf(1300), player.getMoney()); + } + + @Test + public void testGetters() { + Player player = new Player("name", BigDecimal.valueOf(1000)); + assertEquals("name", player.getName()); + assertEquals(BigDecimal.valueOf(1000), player.getMoney()); + assertTrue(player.getPortfolio().getShares().isEmpty()); + assertTrue(player.getTransactionArchive().isEmpty()); + } + + @Test + public void testNullsAndInvalid() { + assertThrows(IllegalArgumentException.class, () -> new Player(null, BigDecimal.valueOf(1000))); + assertThrows(IllegalArgumentException.class, () -> new Player("", BigDecimal.valueOf(1000))); + assertThrows(IllegalArgumentException.class, () -> new Player("name", null)); + assertThrows(IllegalArgumentException.class, () -> new Player("name", BigDecimal.valueOf(-1))); + } +} diff --git a/src/test/java/millions/PortfolioTest.java b/src/test/java/millions/PortfolioTest.java index 3fb1019..69bb678 100644 --- a/src/test/java/millions/PortfolioTest.java +++ b/src/test/java/millions/PortfolioTest.java @@ -2,4 +2,63 @@ import static org.junit.jupiter.api.Assertions.*; -class PortfolioTest {} +import java.math.BigDecimal; +import org.junit.jupiter.api.Test; + +class PortfolioTest { + + @Test + public void happyPath() { + Portfolio portfolio = new Portfolio(); + Stock stock = new Stock("PEAR", "Pear Inc.", BigDecimal.valueOf(300)); + Share share = new Share(stock, 10, BigDecimal.valueOf(300)); + + assertTrue(portfolio.addShare(share)); + assertTrue(portfolio.contains(share)); + assertEquals(1, portfolio.getShares().size()); + assertEquals(share, portfolio.getShares().get(0)); + + assertTrue(portfolio.removeShare(share)); + assertFalse(portfolio.contains(share)); + assertTrue(portfolio.getShares().isEmpty()); + } + + @Test + public void testGettersAndSetters() { + Portfolio portfolio = new Portfolio(); + Stock stock1 = new Stock("PEAR", "Pear Inc.", BigDecimal.valueOf(300)); + Stock stock2 = new Stock("DOGL", "DOOGLE Inc.", BigDecimal.valueOf(200.00)); + Share share1 = new Share(stock1, 10, BigDecimal.valueOf(300)); + Share share2 = new Share(stock2, 5, BigDecimal.valueOf(200)); + + portfolio.addShare(share1); + portfolio.addShare(share2); + + assertEquals(2, portfolio.getShares().size()); + assertTrue(portfolio.getShares().contains(share1)); + assertTrue(portfolio.getShares().contains(share2)); + + assertEquals(1, portfolio.getShares("PEAR").size()); + assertTrue(portfolio.getShares("PEAR").contains(share1)); + assertFalse(portfolio.getShares("PEAR").contains(share2)); + + assertEquals(1, portfolio.getShares("DOGL").size()); + assertFalse(portfolio.getShares("DOGL").contains(share1)); + assertTrue(portfolio.getShares("DOGL").contains(share2)); + + assertTrue(portfolio.getShares("XYZ").isEmpty()); + + portfolio.removeShare(share1); + assertEquals(1, portfolio.getShares().size()); + } + + @Test + public void testNullsAndInvalid() { + Stock stock1 = new Stock("PEAR", "Pear Inc.", BigDecimal.valueOf(300)); + Share share1 = new Share(stock1, 10, BigDecimal.valueOf(300)); + Portfolio portfolio = new Portfolio(); + assertFalse(portfolio.removeShare(null)); + assertFalse(portfolio.contains(null)); + assertFalse(portfolio.removeShare(share1)); + } +} diff --git a/src/test/java/millions/PurchaseTest.java b/src/test/java/millions/PurchaseTest.java index a67c37a..4cbb6cc 100644 --- a/src/test/java/millions/PurchaseTest.java +++ b/src/test/java/millions/PurchaseTest.java @@ -2,4 +2,39 @@ import static org.junit.jupiter.api.Assertions.*; -class PurchaseTest {} +import java.math.BigDecimal; +import org.junit.jupiter.api.Test; + +class PurchaseTest { + @Test + public void testHappyPath() { + Stock stock = new Stock("TestStock", "TST", BigDecimal.valueOf(10)); + Share share = new Share(stock, 2, BigDecimal.valueOf(10)); + Player player = new Player("TestPlayer", BigDecimal.valueOf(100)); + Purchase purchase = new Purchase(share, 1); + purchase.commit(player); + assertTrue(purchase.isCommitted()); + + // 1 less because of tax + assertEquals(79, player.getMoney().intValue()); + assertTrue(player.getPortfolio().getShares().contains(share)); + } + + @Test + public void testNullsAndInvalid() { + + Stock stock = new Stock("TestStock", "TST", BigDecimal.valueOf(10)); + Share share = new Share(stock, 2, BigDecimal.valueOf(10)); + Player player = new Player("TestPlayer", BigDecimal.valueOf(100)); + Purchase purchase = new Purchase(share, 1); + + // Double commit + purchase.commit(player); + assertThrows(IllegalStateException.class, () -> purchase.commit(player)); + + // Insufficient funds + Player poorPlayer = new Player("PoorPlayer", BigDecimal.valueOf(10)); + Purchase expensivePurchase = new Purchase(new Share(stock, 20, BigDecimal.valueOf(10)), 1); + assertThrows(IllegalStateException.class, () -> expensivePurchase.commit(poorPlayer)); + } +} diff --git a/src/test/java/millions/SaleTest.java b/src/test/java/millions/SaleTest.java index b808f7a..a9202b7 100644 --- a/src/test/java/millions/SaleTest.java +++ b/src/test/java/millions/SaleTest.java @@ -2,4 +2,31 @@ import static org.junit.jupiter.api.Assertions.*; -class SaleTest {} +import java.math.BigDecimal; +import org.junit.jupiter.api.Test; + +class SaleTest { + + @Test + public void testHappyPath() { + Stock stock = new Stock("TestStock", "TST", BigDecimal.valueOf(10)); + Share share = new Share(stock, 2, BigDecimal.valueOf(10)); + Player player = new Player("TestPlayer", BigDecimal.valueOf(100)); + player.getPortfolio().addShare(share); + Sale sale = new Sale(share, 1); + sale.commit(player); + assertTrue(sale.isCommitted()); + + assertEquals(120, player.getMoney().intValue()); + assertFalse(player.getPortfolio().getShares().contains(share)); + } + + @Test + public void testNullsAndInvalid() { + Stock stock = new Stock("TestStock", "TST", BigDecimal.valueOf(10)); + Share share = new Share(stock, 2, BigDecimal.valueOf(10)); + Player player = new Player("TestPlayer", BigDecimal.valueOf(100)); + Sale sale = new Sale(share, 1); + assertThrows(IllegalStateException.class, () -> sale.commit(player)); + } +} diff --git a/src/test/java/millions/ShareTest.java b/src/test/java/millions/ShareTest.java index cf3d676..0925c5b 100644 --- a/src/test/java/millions/ShareTest.java +++ b/src/test/java/millions/ShareTest.java @@ -2,4 +2,33 @@ import static org.junit.jupiter.api.Assertions.*; -class ShareTest {} +import java.math.BigDecimal; +import org.junit.jupiter.api.Test; + +class ShareTest { + + @Test + public void testHappyPath() { + Stock stock = new Stock("AYO", "Ayhoo", BigDecimal.valueOf(10)); + Share share = new Share(stock, 5, BigDecimal.valueOf(10)); + assertEquals(BigDecimal.valueOf(5), share.getQuantity()); + } + + @Test + public void testGetters() { + Stock stock = new Stock("AYO", "Ayhoo", BigDecimal.valueOf(10)); + Share share = new Share(stock, 5, BigDecimal.valueOf(10)); + assertEquals(BigDecimal.valueOf(5), share.getQuantity()); + assertEquals(stock, share.getStock()); + assertEquals(BigDecimal.valueOf(10), share.getPurchasePrice()); + } + + @Test + public void testNullsAndInvalid() { + Stock stock = new Stock("AYO", "Ayhoo", BigDecimal.valueOf(10)); + assertThrows(IllegalArgumentException.class, () -> new Share(null, 2, BigDecimal.valueOf(2))); + assertThrows(IllegalArgumentException.class, () -> new Share(stock, 2, null)); + assertThrows( + IllegalArgumentException.class, () -> new Share(stock, null, BigDecimal.valueOf(2))); + } +} diff --git a/src/test/java/millions/StockTest.java b/src/test/java/millions/StockTest.java index eda8962..13a95a8 100644 --- a/src/test/java/millions/StockTest.java +++ b/src/test/java/millions/StockTest.java @@ -2,4 +2,39 @@ import static org.junit.jupiter.api.Assertions.*; -class StockTest {} +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +class StockTest { + @Test + public void testHapyPath() { + Stock stock = new Stock("NVDA", "Nvadia", BigDecimal.valueOf(20)); + Stock stock2 = + new Stock( + "NVDA", "Nvadia", new ArrayList(Arrays.asList(BigDecimal.valueOf(20)))); + stock2.addNewSalesPrice(BigDecimal.valueOf(30)); + assertEquals(BigDecimal.valueOf(30), stock2.getSalesPrice()); + } + + @Test + public void settersAndGetters() { + Stock stock = new Stock("NVDA", "Nvadia", BigDecimal.valueOf(20)); + assertEquals("NVDA", stock.getSymbol()); + assertEquals("Nvadia", stock.getCompany()); + } + + @Test + public void testNullsAndInvalid() { + + assertThrows( + IllegalArgumentException.class, () -> new Stock(null, "Nvadia", BigDecimal.valueOf(20))); + assertThrows( + IllegalArgumentException.class, () -> new Stock("NVDA", null, BigDecimal.valueOf(20))); + assertThrows( + IllegalArgumentException.class, () -> new Stock("NVDA", "", BigDecimal.valueOf(20))); + assertThrows( + IllegalArgumentException.class, () -> new Stock("", "Nvadia", BigDecimal.valueOf(20))); + } +} diff --git a/src/test/java/millions/TransactionArchiveTest.java b/src/test/java/millions/TransactionArchiveTest.java index bdc9ca8..64d1c2c 100644 --- a/src/test/java/millions/TransactionArchiveTest.java +++ b/src/test/java/millions/TransactionArchiveTest.java @@ -2,4 +2,45 @@ import static org.junit.jupiter.api.Assertions.*; -class TransactionArchiveTest {} +import java.math.BigDecimal; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class TransactionArchiveTest { + + TransactionArchive archive; + Purchase purchase; + Sale sale; + + @BeforeEach + public void setUp() { + archive = new TransactionArchive(); + Stock stock = new Stock("SUS", "Samsung", BigDecimal.valueOf(10)); + Share share = new Share(stock, 2, BigDecimal.valueOf(10)); + purchase = new Purchase(share, 1); + sale = new Sale(share, 2); + } + + @Test + public void testHappyPath() { + assertTrue(archive.isEmpty()); + archive.add(purchase); + assertFalse(archive.isEmpty()); + } + + @Test + public void testNullsAndInvalid() { + archive.add(purchase); + assertFalse(archive.add(purchase)); + } + + @Test + public void testGetters() { + archive.add(purchase); + archive.add(sale); + assertEquals(List.of(purchase), archive.getPurchases(1)); + assertEquals(List.of(sale), archive.getSales(2)); + assertEquals(2, archive.countDistinctWeeks()); + } +} diff --git a/src/test/java/millions/TransactionTest.java b/src/test/java/millions/TransactionTest.java deleted file mode 100644 index 9074225..0000000 --- a/src/test/java/millions/TransactionTest.java +++ /dev/null @@ -1,5 +0,0 @@ -package millions; - -import static org.junit.jupiter.api.Assertions.*; - -class TransactionTest {}