From 5eb6c992abe8d199089f7be1d586b2c08ca6df4e Mon Sep 17 00:00:00 2001 From: Elisabeth Berg Date: Mon, 25 May 2026 14:16:28 +0200 Subject: [PATCH] Added Checkstyle and Javadocs to test-classes --- src/test/java/ExchangeTest.java | 604 +++++++++++++--------- src/test/java/PlayerTest.java | 289 ++++++----- src/test/java/PortfolioTest.java | 344 ++++++------ src/test/java/PurchaseTest.java | 215 ++++---- src/test/java/SaleTest.java | 217 ++++---- src/test/java/ShareTest.java | 205 +++++--- src/test/java/StockTest.java | 374 ++++++++------ src/test/java/TransactionArchiveTest.java | 316 ++++++----- target/test-classes/ExchangeTest.class | Bin 10397 -> 10733 bytes target/test-classes/PortfolioTest.class | Bin 5661 -> 5661 bytes target/test-classes/ShareTest.class | Bin 4137 -> 4255 bytes target/test-classes/StockTest.class | Bin 6116 -> 6225 bytes 12 files changed, 1501 insertions(+), 1063 deletions(-) diff --git a/src/test/java/ExchangeTest.java b/src/test/java/ExchangeTest.java index 9261bd6..4aa8f3a 100644 --- a/src/test/java/ExchangeTest.java +++ b/src/test/java/ExchangeTest.java @@ -1,265 +1,365 @@ -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; import Model.Exchange; import Model.Player; +import Model.Purchase; +import Model.Sale; import Model.Share; import Model.Stock; import Model.Transaction; -import Model.Purchase; -import Model.Sale; - -import static org.junit.jupiter.api.Assertions.*; - import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +/** + * Unit tests for the Exchange class. + * Tests core exchange functionality including stock management, buying/selling, + * market data retrieval, and error handling. + */ public class ExchangeTest { - private Exchange exchange; - private Stock apple; - private Stock google; - private Player player; - - @BeforeEach - void setUp() { - apple = new Stock("AAPL", "Apple", new BigDecimal("100")); - google = new Stock("GOOGL", "Google", new BigDecimal("200")); - - List stocks = new ArrayList<>(); - stocks.add(apple); - stocks.add(google); - - exchange = new Exchange("TestExchange", stocks); - player = new Player("Jane", new BigDecimal("500000")); - } - - // ---- Positive tests ---- - - @Test - void testGetName() { - assertEquals("TestExchange", exchange.getName()); - } - - @Test - void testInitialWeekIsOne() { - assertEquals(1, exchange.getWeek()); - } - - @Test - void testAdvanceIncrementsWeek() { - exchange.advance(); - assertEquals(2, exchange.getWeek()); - } - - @Test - void testHasStockReturnsTrue() { - assertTrue(exchange.hasStock("AAPL")); - } - - @Test - void testHasStockReturnsFalse() { - assertFalse(exchange.hasStock("MSFT")); - } - - @Test - void testGetStockReturnsCorrectStock() { - assertEquals(apple, exchange.getStock("AAPL")); - } - - @Test - void testGetStockReturnsNullForUnknown() { - assertNull(exchange.getStock("MSFT")); - } - - @Test - void testFindStocksBySymbol() { - List result = exchange.findStocks("AAPL"); - assertEquals(1, result.size()); - assertEquals("AAPL", result.get(0).getSymbol()); - } - - @Test - void testFindStocksByCompanyName() { - List result = exchange.findStocks("e"); - assertEquals(2, result.size()); - } - - @Test - void testFindStocksNoMatch() { - List result = exchange.findStocks("Samsung"); - assertEquals(0, result.size()); - } - - @Test - void testFindStocksEmptyTermReturnsAll() { - List result = exchange.findStocks(""); - assertEquals(2, result.size()); - } - - @Test - void testBuyReturnsCommittedPurchase() { - Transaction t = exchange.buy("AAPL", new BigDecimal("5"), player); - assertNotNull(t); - assertTrue(t.isCommitted()); - assertInstanceOf(Purchase.class, t); - } - - @Test - void testBuyDeductsMoneyFromPlayer() { - BigDecimal before = player.getMoney(); - exchange.buy("AAPL", new BigDecimal("5"), player); - assertTrue(player.getMoney().compareTo(before) < 0); - } - - @Test - void testBuyAddsShareToPortfolio() { - exchange.buy("AAPL", new BigDecimal("5"), player); - assertFalse(player.getPortfolio().getShares("AAPL").isEmpty()); - } - - @Test - void testSellReturnsCommittedSale() { - exchange.buy("AAPL", new BigDecimal("5"), player); - Share share = player.getPortfolio().getShares("AAPL").get(0); - - Transaction t = exchange.sell(share, player); - assertNotNull(t); - assertTrue(t.isCommitted()); - assertInstanceOf(Sale.class, t); - } - - @Test - void testSellAddsMoney() { - exchange.buy("AAPL", new BigDecimal("5"), player); - Share share = player.getPortfolio().getShares("AAPL").get(0); - BigDecimal before = player.getMoney(); - - exchange.sell(share, player); - assertTrue(player.getMoney().compareTo(before) > 0); - } - - @Test - void testSellRemovesShareFromPortfolio() { - exchange.buy("AAPL", new BigDecimal("5"), player); - Share share = player.getPortfolio().getShares("AAPL").get(0); - - exchange.sell(share, player); - assertTrue(player.getPortfolio().getShares("AAPL").isEmpty()); - } - - @Test - void testGetGainersReturnsRequestedLimit() { - exchange.advance(); // prices must have changed at least once for a meaningful sort - List gainers = exchange.getGainers(1); - assertEquals(1, gainers.size()); - } - - @Test - void testGetLosersReturnsRequestedLimit() { - exchange.advance(); - List losers = exchange.getLosers(1); - assertEquals(1, losers.size()); - } - - // ---- Negative tests ---- - - @Test - void testConstructorNullNameThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Exchange(null, List.of(apple)) - ); - } - - @Test - void testConstructorBlankNameThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Exchange(" ", List.of(apple)) - ); - } - - @Test - void testConstructorNullStockListThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Exchange("X", null) - ); - } - - @Test - void testConstructorNullStockEntryThrows() { - List withNull = new ArrayList<>(); - withNull.add(apple); - withNull.add(null); - assertThrows(IllegalArgumentException.class, () -> - new Exchange("X", withNull) - ); - } - - @Test - void testBuyNullSymbolThrows() { - assertThrows(IllegalArgumentException.class, () -> - exchange.buy(null, new BigDecimal("5"), player) - ); - } - - @Test - void testBuyUnknownSymbolThrows() { - assertThrows(IllegalArgumentException.class, () -> - exchange.buy("MSFT", new BigDecimal("5"), player) - ); - } - - @Test - void testBuyZeroQuantityThrows() { - assertThrows(IllegalArgumentException.class, () -> - exchange.buy("AAPL", BigDecimal.ZERO, player) - ); - } - - @Test - void testBuyNegativeQuantityThrows() { - assertThrows(IllegalArgumentException.class, () -> - exchange.buy("AAPL", new BigDecimal("-1"), player) - ); - } - - @Test - void testBuyNullPlayerThrows() { - assertThrows(IllegalArgumentException.class, () -> - exchange.buy("AAPL", new BigDecimal("5"), null) - ); - } - - @Test - void testBuyInsufficientFundsThrows() { - Player poorPlayer = new Player("Broke", new BigDecimal("1")); - assertThrows(IllegalStateException.class, () -> - exchange.buy("AAPL", new BigDecimal("5"), poorPlayer) - ); - } - - @Test - void testSellNullPlayerThrows() { - exchange.buy("AAPL", new BigDecimal("5"), player); - Share share = player.getPortfolio().getShares("AAPL").get(0); - assertThrows(IllegalArgumentException.class, () -> - exchange.sell(share, null) - ); - } - - @Test - void testSellShareNotInPortfolioThrows() { - Share unowned = new Share(apple, new BigDecimal("1"), new BigDecimal("100")); - assertThrows(IllegalStateException.class, () -> - exchange.sell(unowned, player) - ); - } - - @Test - void testAddNullObserverThrows() { - assertThrows(IllegalArgumentException.class, () -> - exchange.addObserver(null) - ); - } + private Exchange exchange; + private Stock apple; + private Stock google; + private Player player; + + @BeforeEach + void setUp() { + apple = new Stock("AAPL", "Apple", new BigDecimal("100")); + google = new Stock("GOOGL", "Google", new BigDecimal("200")); + + List stocks = new ArrayList<>(); + stocks.add(apple); + stocks.add(google); + + exchange = new Exchange("TestExchange", stocks); + player = new Player("Jane", new BigDecimal("500000")); + } + + // ---- Positive tests ---- + + /** + * Tests that exchange returns its correct name. + */ + @Test + void testGetName() { + assertEquals("TestExchange", exchange.getName()); + } + + /** + * Tests that exchange starts at week 1. + */ + @Test + void testInitialWeekIsOne() { + assertEquals(1, exchange.getWeek()); + } + + /** + * Tests that calling advance increments the week counter. + */ + @Test + void testAdvanceIncrementsWeek() { + exchange.advance(); + assertEquals(2, exchange.getWeek()); + } + + /** + * Tests that hasStock returns true for a known stock symbol. + */ + @Test + void testHasStockReturnsTrue() { + assertTrue(exchange.hasStock("AAPL")); + } + + /** + * Tests that hasStock returns false for an unknown stock symbol. + */ + @Test + void testHasStockReturnsFalse() { + assertFalse(exchange.hasStock("MSFT")); + } + + /** + * Tests that getStock returns the correct Stock object for a given symbol. + */ + @Test + void testGetStockReturnsCorrectStock() { + assertEquals(apple, exchange.getStock("AAPL")); + } + + /** + * Tests that getStock returns null for an unknown symbol. + */ + @Test + void testGetStockReturnsNullForUnknown() { + assertNull(exchange.getStock("MSFT")); + } + + /** + * Tests that findStocks correctly locates stocks by symbol. + */ + @Test + void testFindStocksBySymbol() { + List result = exchange.findStocks("AAPL"); + assertEquals(1, result.size()); + assertEquals("AAPL", result.get(0).getSymbol()); + } + + /** + * Tests that findStocks correctly locates stocks by partial company name match. + */ + @Test + void testFindStocksByCompanyName() { + List result = exchange.findStocks("e"); + assertEquals(2, result.size()); + } + + /** + * Tests that findStocks returns empty list when no stocks match search term. + */ + @Test + void testFindStocksNoMatch() { + List result = exchange.findStocks("Samsung"); + assertEquals(0, result.size()); + } + + /** + * Tests that findStocks with empty search term returns all stocks. + */ + @Test + void testFindStocksEmptyTermReturnsAll() { + List result = exchange.findStocks(""); + assertEquals(2, result.size()); + } + + /** + * Tests that buying a stock returns a committed Purchase transaction. + */ + @Test + void testBuyReturnsCommittedPurchase() { + Transaction t = exchange.buy("AAPL", new BigDecimal("5"), player); + assertNotNull(t); + assertTrue(t.isCommitted()); + assertInstanceOf(Purchase.class, t); + } + + /** + * Tests that buying stock deducts the correct amount from player's balance. + */ + @Test + void testBuyDeductsMoneyFromPlayer() { + BigDecimal before = player.getMoney(); + exchange.buy("AAPL", new BigDecimal("5"), player); + assertTrue(player.getMoney().compareTo(before) < 0); + } + + /** + * Tests that buying stock adds the share to player's portfolio. + */ + @Test + void testBuyAddsShareToPortfolio() { + exchange.buy("AAPL", new BigDecimal("5"), player); + assertFalse(player.getPortfolio().getShares("AAPL").isEmpty()); + } + + /** + * Tests that selling a share returns a committed Sale transaction. + */ + @Test + void testSellReturnsCommittedSale() { + exchange.buy("AAPL", new BigDecimal("5"), player); + Share share = player.getPortfolio().getShares("AAPL").get(0); + + Transaction t = exchange.sell(share, player); + assertNotNull(t); + assertTrue(t.isCommitted()); + assertInstanceOf(Sale.class, t); + } + + /** + * Tests that selling stock adds the correct amount to player's balance. + */ + @Test + void testSellAddsMoney() { + exchange.buy("AAPL", new BigDecimal("5"), player); + Share share = player.getPortfolio().getShares("AAPL").get(0); + BigDecimal before = player.getMoney(); + + exchange.sell(share, player); + assertTrue(player.getMoney().compareTo(before) > 0); + } + + /** + * Tests that selling stock removes the share from player's portfolio. + */ + @Test + void testSellRemovesShareFromPortfolio() { + exchange.buy("AAPL", new BigDecimal("5"), player); + Share share = player.getPortfolio().getShares("AAPL").get(0); + + exchange.sell(share, player); + assertTrue(player.getPortfolio().getShares("AAPL").isEmpty()); + } + + /** + * Tests that getGainers returns the requested number of gaining stocks. + */ + @Test + void testGetGainersReturnsRequestedLimit() { + exchange.advance(); // prices must have changed at least once for a meaningful sort + List gainers = exchange.getGainers(1); + assertEquals(1, gainers.size()); + } + + /** + * Tests that getLosers returns the requested number of losing stocks. + */ + @Test + void testGetLosersReturnsRequestedLimit() { + exchange.advance(); + List losers = exchange.getLosers(1); + assertEquals(1, losers.size()); + } + + // ---- Negative tests ---- + + /** + * Tests that Exchange constructor throws when given a null name. + */ + @Test + void testConstructorNullNameThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Exchange(null, List.of(apple)) + ); + } + + /** + * Tests that Exchange constructor throws when given a blank name. + */ + @Test + void testConstructorBlankNameThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Exchange(" ", List.of(apple)) + ); + } + + /** + * Tests that Exchange constructor throws when given a null stock list. + */ + @Test + void testConstructorNullStockListThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Exchange("X", null) + ); + } + + /** + * Tests that Exchange constructor throws when stock list contains null entry. + */ + @Test + void testConstructorNullStockEntryThrows() { + List withNull = new ArrayList<>(); + withNull.add(apple); + withNull.add(null); + assertThrows(IllegalArgumentException.class, () -> + new Exchange("X", withNull) + ); + } + + /** + * Tests that buy throws when given a null symbol. + */ + @Test + void testBuyNullSymbolThrows() { + assertThrows(IllegalArgumentException.class, () -> + exchange.buy(null, new BigDecimal("5"), player) + ); + } + + /** + * Tests that buy throws when given an unknown stock symbol. + */ + @Test + void testBuyUnknownSymbolThrows() { + assertThrows(IllegalArgumentException.class, () -> + exchange.buy("MSFT", new BigDecimal("5"), player) + ); + } + + /** + * Tests that buy throws when given zero quantity. + */ + @Test + void testBuyZeroQuantityThrows() { + assertThrows(IllegalArgumentException.class, () -> + exchange.buy("AAPL", BigDecimal.ZERO, player) + ); + } + + /** + * Tests that buy throws when given negative quantity. + */ + @Test + void testBuyNegativeQuantityThrows() { + assertThrows(IllegalArgumentException.class, () -> + exchange.buy("AAPL", new BigDecimal("-1"), player) + ); + } + + /** + * Tests that buy throws when given a null player. + */ + @Test + void testBuyNullPlayerThrows() { + assertThrows(IllegalArgumentException.class, () -> + exchange.buy("AAPL", new BigDecimal("5"), null) + ); + } + + /** + * Tests that buy throws when player has insufficient funds. + */ + @Test + void testBuyInsufficientFundsThrows() { + Player poorPlayer = new Player("Broke", new BigDecimal("1")); + assertThrows(IllegalStateException.class, () -> + exchange.buy("AAPL", new BigDecimal("5"), poorPlayer) + ); + } + + /** + * Tests that sell throws when given a null player. + */ + @Test + void testSellNullPlayerThrows() { + exchange.buy("AAPL", new BigDecimal("5"), player); + Share share = player.getPortfolio().getShares("AAPL").get(0); + assertThrows(IllegalArgumentException.class, () -> + exchange.sell(share, null) + ); + } + + /** + * Tests that sell throws when share is not in player's portfolio. + */ + @Test + void testSellShareNotInPortfolioThrows() { + Share unowned = new Share(apple, new BigDecimal("1"), new BigDecimal("100")); + assertThrows(IllegalStateException.class, () -> + exchange.sell(unowned, player) + ); + } + + /** + * Tests that addObserver throws when given a null observer. + */ + @Test + void testAddNullObserverThrows() { + assertThrows(IllegalArgumentException.class, () -> + exchange.addObserver(null) + ); + } } + diff --git a/src/test/java/PlayerTest.java b/src/test/java/PlayerTest.java index ac1f95a..880ac8f 100644 --- a/src/test/java/PlayerTest.java +++ b/src/test/java/PlayerTest.java @@ -2,127 +2,182 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - import Model.Player; import java.math.BigDecimal; +/** + * Unit tests for the Player class. + * Tests player creation, money management, portfolio, and error handling. + */ public class PlayerTest { - private Player player; - - @BeforeEach - void setUp() { - player = new Player("Jane", new BigDecimal("1000")); - } - - // ---- Positive tests ---- - - @Test - void testGetName() { - assertEquals("Jane", player.getName()); - } - - @Test - void testGetMoneyMatchesStartingCapital() { - assertEquals(new BigDecimal("1000"), player.getMoney()); - } - - @Test - void testAddMoney() { - player.addMoney(new BigDecimal("500")); - assertEquals(new BigDecimal("1500"), player.getMoney()); - } - - @Test - void testAddMoneyZero() { - player.addMoney(BigDecimal.ZERO); - assertEquals(new BigDecimal("1000"), player.getMoney()); - } - - @Test - void testWithdrawMoney() { - player.withdrawMoney(new BigDecimal("400")); - assertEquals(new BigDecimal("600"), player.getMoney()); - } - - @Test - void testWithdrawMoneyZero() { - player.withdrawMoney(BigDecimal.ZERO); - assertEquals(new BigDecimal("1000"), player.getMoney()); - } - - @Test - void testPortfolioInitiallyEmpty() { - assertTrue(player.getPortfolio().getShares().isEmpty()); - } - - @Test - void testTransactionArchiveInitiallyEmpty() { - assertTrue(player.getTransactionArchive().isEmpty()); - } - - @Test - void testStartingMoneyZeroAllowed() { - Player broke = new Player("Broke", BigDecimal.ZERO); - assertEquals(BigDecimal.ZERO, broke.getMoney()); - } - - // ---- Negative tests ---- - - @Test - void testNullNameThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Player(null, new BigDecimal("1000")) - ); - } - - @Test - void testBlankNameThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Player(" ", new BigDecimal("1000")) - ); - } - - @Test - void testNullStartingMoneyThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Player("Jane", null) - ); - } - - @Test - void testNegativeStartingMoneyThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Player("Jane", new BigDecimal("-1")) - ); - } - - @Test - void testAddNullAmountThrows() { - assertThrows(IllegalArgumentException.class, () -> - player.addMoney(null) - ); - } - - @Test - void testAddNegativeAmountThrows() { - assertThrows(IllegalArgumentException.class, () -> - player.addMoney(new BigDecimal("-100")) - ); - } - - @Test - void testWithdrawNullAmountThrows() { - assertThrows(IllegalArgumentException.class, () -> - player.withdrawMoney(null) - ); - } - - @Test - void testWithdrawNegativeAmountThrows() { - assertThrows(IllegalArgumentException.class, () -> - player.withdrawMoney(new BigDecimal("-100")) - ); - } + private Player player; + + @BeforeEach + void setUp() { + player = new Player("Jane", new BigDecimal("1000")); + } + + // ---- Positive tests ---- + + /** + * Tests that player name is stored and retrieved correctly. + */ + @Test + void testGetName() { + assertEquals("Jane", player.getName()); + } + + /** + * Tests that player's initial money equals starting capital. + */ + @Test + void testGetMoneyMatchesStartingCapital() { + assertEquals(new BigDecimal("1000"), player.getMoney()); + } + + /** + * Tests that adding money increases player's balance. + */ + @Test + void testAddMoney() { + player.addMoney(new BigDecimal("500")); + assertEquals(new BigDecimal("1500"), player.getMoney()); + } + + /** + * Tests that adding zero money does not change balance. + */ + @Test + void testAddMoneyZero() { + player.addMoney(BigDecimal.ZERO); + assertEquals(new BigDecimal("1000"), player.getMoney()); + } + + /** + * Tests that withdrawing money decreases player's balance. + */ + @Test + void testWithdrawMoney() { + player.withdrawMoney(new BigDecimal("400")); + assertEquals(new BigDecimal("600"), player.getMoney()); + } + + /** + * Tests that withdrawing zero money does not change balance. + */ + @Test + void testWithdrawMoneyZero() { + player.withdrawMoney(BigDecimal.ZERO); + assertEquals(new BigDecimal("1000"), player.getMoney()); + } + + /** + * Tests that player's portfolio is initially empty. + */ + @Test + void testPortfolioInitiallyEmpty() { + assertTrue(player.getPortfolio().getShares().isEmpty()); + } + + /** + * Tests that player's transaction archive is initially empty. + */ + @Test + void testTransactionArchiveInitiallyEmpty() { + assertTrue(player.getTransactionArchive().isEmpty()); + } + + /** + * Tests that player can be created with zero starting money. + */ + @Test + void testStartingMoneyZeroAllowed() { + Player broke = new Player("Broke", BigDecimal.ZERO); + assertEquals(BigDecimal.ZERO, broke.getMoney()); + } + + // ---- Negative tests ---- + + /** + * Tests that Player constructor throws when given a null name. + */ + @Test + void testNullNameThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Player(null, new BigDecimal("1000")) + ); + } + + /** + * Tests that Player constructor throws when given a blank name. + */ + @Test + void testBlankNameThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Player(" ", new BigDecimal("1000")) + ); + } + + /** + * Tests that Player constructor throws when given null starting money. + */ + @Test + void testNullStartingMoneyThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Player("Jane", null) + ); + } + + /** + * Tests that Player constructor throws when given negative starting money. + */ + @Test + void testNegativeStartingMoneyThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Player("Jane", new BigDecimal("-1")) + ); + } + + /** + * Tests that addMoney throws when given null amount. + */ + @Test + void testAddNullAmountThrows() { + assertThrows(IllegalArgumentException.class, () -> + player.addMoney(null) + ); + } + + /** + * Tests that addMoney throws when given negative amount. + */ + @Test + void testAddNegativeAmountThrows() { + assertThrows(IllegalArgumentException.class, () -> + player.addMoney(new BigDecimal("-100")) + ); + } + + /** + * Tests that withdrawMoney throws when given null amount. + */ + @Test + void testWithdrawNullAmountThrows() { + assertThrows(IllegalArgumentException.class, () -> + player.withdrawMoney(null) + ); + } + + /** + * Tests that withdrawMoney throws when given negative amount. + */ + @Test + void testWithdrawNegativeAmountThrows() { + assertThrows(IllegalArgumentException.class, () -> + player.withdrawMoney(new BigDecimal("-100")) + ); + } } + diff --git a/src/test/java/PortfolioTest.java b/src/test/java/PortfolioTest.java index 34e46aa..d55259d 100644 --- a/src/test/java/PortfolioTest.java +++ b/src/test/java/PortfolioTest.java @@ -1,155 +1,209 @@ import static org.junit.jupiter.api.Assertions.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - import Model.Portfolio; import Model.Share; import Model.Stock; - import java.math.BigDecimal; import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +/** + * Unit tests for the Portfolio class. + * Tests share management, portfolio queries, net worth calculation, and error + * handling. + */ public class PortfolioTest { - private Portfolio portfolio; - private Stock stock; - private Share share; - - @BeforeEach - void setUp() { - portfolio = new Portfolio(); - stock = new Stock("AAPL", "Apple", new BigDecimal("150")); - share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); - } - - // ---- Positive tests ---- - - @Test - void testAddShare() { - boolean result = portfolio.addShare(share); - assertTrue(result); - assertTrue(portfolio.contains(share)); - } - - @Test - void testAddMultipleShares() { - Stock stock2 = new Stock("GOOGL", "Google", new BigDecimal("200")); - Share share2 = new Share(stock2, new BigDecimal("5"), new BigDecimal("190")); - - portfolio.addShare(share); - portfolio.addShare(share2); - - assertEquals(2, portfolio.getShares().size()); - } - - @Test - void testRemoveShare() { - portfolio.addShare(share); - boolean result = portfolio.removeShare(share); - assertTrue(result); - assertFalse(portfolio.contains(share)); - } - - @Test - void testRemoveShareNotInPortfolioReturnsFalse() { - boolean result = portfolio.removeShare(share); - assertFalse(result); - } - - @Test - void testGetSharesReturnsAll() { - portfolio.addShare(share); - List shares = portfolio.getShares(); - assertEquals(1, shares.size()); - assertTrue(shares.contains(share)); - } - - @Test - void testGetSharesIsDefensiveCopy() { - portfolio.addShare(share); - List copy = portfolio.getShares(); - copy.clear(); - assertEquals(1, portfolio.getShares().size()); - } - - @Test - void testGetSharesBySymbol() { - Stock stock2 = new Stock("GOOGL", "Google", new BigDecimal("200")); - Share share2 = new Share(stock2, new BigDecimal("5"), new BigDecimal("190")); - - portfolio.addShare(share); - portfolio.addShare(share2); - - List result = portfolio.getShares("AAPL"); - assertEquals(1, result.size()); - assertTrue(result.contains(share)); - } - - @Test - void testGetSharesBySymbolNoMatch() { - portfolio.addShare(share); - List result = portfolio.getShares("MSFT"); - assertTrue(result.isEmpty()); - } - - @Test - void testContainsReturnsTrueWhenPresent() { - portfolio.addShare(share); - assertTrue(portfolio.contains(share)); - } - - @Test - void testContainsReturnsFalseWhenAbsent() { - assertFalse(portfolio.contains(share)); - } - - @Test - void testGetNetWorthEmptyPortfolio() { - assertEquals(BigDecimal.ZERO, portfolio.getNetWorth()); - } - - @Test - void testGetNetWorthWithShares() { - portfolio.addShare(share); - // net worth should be positive when portfolio has holdings - assertTrue(portfolio.getNetWorth().compareTo(BigDecimal.ZERO) > 0); - } - - // ---- Negative tests ---- - - @Test - void testAddNullShareThrows() { - assertThrows(IllegalArgumentException.class, () -> - portfolio.addShare(null) - ); - } - - @Test - void testRemoveNullShareThrows() { - assertThrows(IllegalArgumentException.class, () -> - portfolio.removeShare(null) - ); - } - - @Test - void testContainsNullShareThrows() { - assertThrows(IllegalArgumentException.class, () -> - portfolio.contains(null) - ); - } - - @Test - void testGetSharesByNullSymbolThrows() { - assertThrows(IllegalArgumentException.class, () -> - portfolio.getShares((String) null) - ); - } - - @Test - void testGetSharesByBlankSymbolThrows() { - assertThrows(IllegalArgumentException.class, () -> - portfolio.getShares(" ") - ); - } + private Portfolio portfolio; + private Stock stock; + private Share share; + + @BeforeEach + void setUp() { + portfolio = new Portfolio(); + stock = new Stock("AAPL", "Apple", new BigDecimal("150")); + share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); + } + + // ---- Positive tests ---- + + /** + * Tests that adding a share to portfolio returns true and succeeds. + */ + @Test + void testAddShare() { + boolean result = portfolio.addShare(share); + assertTrue(result); + assertTrue(portfolio.contains(share)); + } + + /** + * Tests that portfolio can store multiple shares from different stocks. + */ + @Test + void testAddMultipleShares() { + Stock stock2 = new Stock("GOOGL", "Google", new BigDecimal("200")); + Share share2 = new Share(stock2, new BigDecimal("5"), new BigDecimal("190")); + + portfolio.addShare(share); + portfolio.addShare(share2); + + assertEquals(2, portfolio.getShares().size()); + } + + /** + * Tests that removing a share from portfolio returns true and succeeds. + */ + @Test + void testRemoveShare() { + portfolio.addShare(share); + boolean result = portfolio.removeShare(share); + assertTrue(result); + assertFalse(portfolio.contains(share)); + } + + /** + * Tests that removing a share not in portfolio returns false. + */ + @Test + void testRemoveShareNotInPortfolioReturnsFalse() { + boolean result = portfolio.removeShare(share); + assertFalse(result); + } + + /** + * Tests that getShares returns all shares in portfolio. + */ + @Test + void testGetSharesReturnsAll() { + portfolio.addShare(share); + List shares = portfolio.getShares(); + assertEquals(1, shares.size()); + assertTrue(shares.contains(share)); + } + + /** + * Tests that getShares returns a defensive copy that cannot modify internal + * state. + */ + @Test + void testGetSharesIsDefensiveCopy() { + portfolio.addShare(share); + List copy = portfolio.getShares(); + copy.clear(); + assertEquals(1, portfolio.getShares().size()); + } + + /** + * Tests that getShares by symbol returns only shares of that stock. + */ + @Test + void testGetSharesBySymbol() { + Stock stock2 = new Stock("GOOGL", "Google", new BigDecimal("200")); + Share share2 = new Share(stock2, new BigDecimal("5"), new BigDecimal("190")); + + portfolio.addShare(share); + portfolio.addShare(share2); + + List result = portfolio.getShares("AAPL"); + assertEquals(1, result.size()); + assertTrue(result.contains(share)); + } + + /** + * Tests that getShares by symbol returns empty list when no matches. + */ + @Test + void testGetSharesBySymbolNoMatch() { + portfolio.addShare(share); + List result = portfolio.getShares("MSFT"); + assertTrue(result.isEmpty()); + } + + /** + * Tests that contains returns true for shares in portfolio. + */ + @Test + void testContainsReturnsTrueWhenPresent() { + portfolio.addShare(share); + assertTrue(portfolio.contains(share)); + } + + /** + * Tests that contains returns false for shares not in portfolio. + */ + @Test + void testContainsReturnsFalseWhenAbsent() { + assertFalse(portfolio.contains(share)); + } + + /** + * Tests that empty portfolio has zero net worth. + */ + @Test + void testGetNetWorthEmptyPortfolio() { + assertEquals(BigDecimal.ZERO, portfolio.getNetWorth()); + } + + /** + * Tests that portfolio with shares has positive net worth. + */ + @Test + void testGetNetWorthWithShares() { + portfolio.addShare(share); + // net worth should be positive when portfolio has holdings + assertTrue(portfolio.getNetWorth().compareTo(BigDecimal.ZERO) > 0); + } + + // ---- Negative tests ---- + + /** + * Tests that addShare throws when given null share. + */ + @Test + void testAddNullShareThrows() { + assertThrows(IllegalArgumentException.class, () -> + portfolio.addShare(null) + ); + } + + /** + * Tests that removeShare throws when given null share. + */ + @Test + void testRemoveNullShareThrows() { + assertThrows(IllegalArgumentException.class, () -> + portfolio.removeShare(null) + ); + } + + /** + * Tests that contains throws when given null share. + */ + @Test + void testContainsNullShareThrows() { + assertThrows(IllegalArgumentException.class, () -> + portfolio.contains(null) + ); + } + + /** + * Tests that getShares throws when given null symbol. + */ + @Test + void testGetSharesByNullSymbolThrows() { + assertThrows(IllegalArgumentException.class, () -> + portfolio.getShares((String) null) + ); + } + + /** + * Tests that getShares throws when given blank symbol. + */ + @Test + void testGetSharesByBlankSymbolThrows() { + assertThrows(IllegalArgumentException.class, () -> + portfolio.getShares(" ") + ); + } } diff --git a/src/test/java/PurchaseTest.java b/src/test/java/PurchaseTest.java index d41e71a..26d2dd9 100644 --- a/src/test/java/PurchaseTest.java +++ b/src/test/java/PurchaseTest.java @@ -4,100 +4,137 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - import Model.Player; import Model.Purchase; import Model.Share; import Model.Stock; +/** + * Unit tests for the Purchase class. + * Tests purchase transaction handling, commitment, and error handling. + */ public class PurchaseTest { - private Stock stock; - private Share share; - private Purchase purchase; - private Player player; - - @BeforeEach - void setUp() { - stock = new Stock("AAPL", "Apple", new BigDecimal("100")); - share = new Share(stock, new BigDecimal("20"), new BigDecimal("50")); - purchase = new Purchase(share, 1); - player = new Player("Jane", new BigDecimal("500000")); - } - - // ---- Positive tests ---- - - @Test - void testCommitDeductsMoneyFromPlayer() { - BigDecimal before = player.getMoney(); - purchase.commit(player); - assertTrue(player.getMoney().compareTo(before) < 0); - } - - @Test - void testCommitAddsShareToPortfolio() { - purchase.commit(player); - assertTrue(player.getPortfolio().contains(share)); - } - - @Test - void testCommitRecordedInArchive() { - purchase.commit(player); - assertTrue(player.getTransactionArchive().getAllTransactions().contains(purchase)); - } - - @Test - void testCommitSetsCommittedFlag() { - purchase.commit(player); - assertTrue(purchase.isCommitted()); - } - - @Test - void testGetShareReturnsCorrectShare() { - assertEquals(share, purchase.getShare()); - } - - @Test - void testGetWeekReturnsCorrectWeek() { - assertEquals(1, purchase.getWeek()); - } - - // ---- Negative tests ---- - - @Test - void testCommitNullPlayerThrows() { - assertThrows(IllegalArgumentException.class, () -> - purchase.commit(null) - ); - } - - @Test - void testCommitInsufficientFundsThrows() { - Player poorPlayer = new Player("Broke", new BigDecimal("1")); - assertThrows(IllegalStateException.class, () -> - purchase.commit(poorPlayer) - ); - } - - @Test - void testCommitAlreadyCommittedThrows() { - purchase.commit(player); - assertThrows(IllegalStateException.class, () -> - purchase.commit(player) - ); - } - - @Test - void testConstructorWeekZeroThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Purchase(share, 0) - ); - } - - @Test - void testConstructorNegativeWeekThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Purchase(share, -5) - ); - } + private Stock stock; + private Share share; + private Purchase purchase; + private Player player; + + @BeforeEach + void setUp() { + stock = new Stock("AAPL", "Apple", new BigDecimal("100")); + share = new Share(stock, new BigDecimal("20"), new BigDecimal("50")); + purchase = new Purchase(share, 1); + player = new Player("Jane", new BigDecimal("500000")); + } + + // ---- Positive tests ---- + + /** + * Tests that committing a purchase deducts money from player's balance. + */ + @Test + void testCommitDeductsMoneyFromPlayer() { + BigDecimal before = player.getMoney(); + purchase.commit(player); + assertTrue(player.getMoney().compareTo(before) < 0); + } + + /** + * Tests that committing a purchase adds the share to player's portfolio. + */ + @Test + void testCommitAddsShareToPortfolio() { + purchase.commit(player); + assertTrue(player.getPortfolio().contains(share)); + } + + /** + * Tests that committed purchase is recorded in transaction archive. + */ + @Test + void testCommitRecordedInArchive() { + purchase.commit(player); + assertTrue(player.getTransactionArchive().getAllTransactions().contains(purchase)); + } + + /** + * Tests that committing a purchase sets the committed flag. + */ + @Test + void testCommitSetsCommittedFlag() { + purchase.commit(player); + assertTrue(purchase.isCommitted()); + } + + /** + * Tests that getShare returns the correct share object. + */ + @Test + void testGetShareReturnsCorrectShare() { + assertEquals(share, purchase.getShare()); + } + + /** + * Tests that getWeek returns the purchase week. + */ + @Test + void testGetWeekReturnsCorrectWeek() { + assertEquals(1, purchase.getWeek()); + } + + // ---- Negative tests ---- + + /** + * Tests that commit throws when given null player. + */ + @Test + void testCommitNullPlayerThrows() { + assertThrows(IllegalArgumentException.class, () -> + purchase.commit(null) + ); + } + + /** + * Tests that commit throws when player has insufficient funds. + */ + @Test + void testCommitInsufficientFundsThrows() { + Player poorPlayer = new Player("Broke", new BigDecimal("1")); + assertThrows(IllegalStateException.class, () -> + purchase.commit(poorPlayer) + ); + } + + /** + * Tests that commit throws when purchase was already committed. + */ + @Test + void testCommitAlreadyCommittedThrows() { + purchase.commit(player); + assertThrows(IllegalStateException.class, () -> + purchase.commit(player) + ); + } + + /** + * Tests that Purchase constructor throws when given zero week. + */ + @Test + void testConstructorWeekZeroThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Purchase(share, 0) + ); + } + + /** + * Tests that Purchase constructor throws when given negative week. + */ + @Test + void testConstructorNegativeWeekThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Purchase(share, -5) + ); + } } + diff --git a/src/test/java/SaleTest.java b/src/test/java/SaleTest.java index 622f262..39d5516 100644 --- a/src/test/java/SaleTest.java +++ b/src/test/java/SaleTest.java @@ -5,100 +5,137 @@ import Model.Sale; import Model.Share; import Model.Stock; - import static org.junit.jupiter.api.Assertions.*; import java.math.BigDecimal; +/** + * Unit tests for the Sale class. + * Tests sale transaction handling, commitment, and error handling. + */ public class SaleTest { - private Stock stock; - private Share share; - private Sale sale; - private Player player; - - @BeforeEach - void setUp() { - stock = new Stock("AAPL", "Apple", new BigDecimal("100")); - share = new Share(stock, new BigDecimal("20"), new BigDecimal("80")); - sale = new Sale(share, 1); - player = new Player("Jane", new BigDecimal("500")); - player.getPortfolio().addShare(share); - } - - // ---- Positive tests ---- - - @Test - void testCommitAddsMoney() { - BigDecimal before = player.getMoney(); - sale.commit(player); - assertTrue(player.getMoney().compareTo(before) > 0); - } - - @Test - void testCommitRemovesShareFromPortfolio() { - sale.commit(player); - assertFalse(player.getPortfolio().contains(share)); - } - - @Test - void testCommitRecordedInArchive() { - sale.commit(player); - assertTrue(player.getTransactionArchive().getAllTransactions().contains(sale)); - } - - @Test - void testCommitSetsCommittedFlag() { - sale.commit(player); - assertTrue(sale.isCommitted()); - } - - @Test - void testGetShareReturnsCorrectShare() { - assertEquals(share, sale.getShare()); - } - - @Test - void testGetWeekReturnsCorrectWeek() { - assertEquals(1, sale.getWeek()); - } - - // ---- Negative tests ---- - - @Test - void testCommitNullPlayerThrows() { - assertThrows(IllegalArgumentException.class, () -> - sale.commit(null) - ); - } - - @Test - void testCommitAlreadyCommittedThrows() { - sale.commit(player); - assertThrows(IllegalStateException.class, () -> - sale.commit(player) - ); - } - - @Test - void testCommitShareNotInPortfolioThrows() { - Player otherPlayer = new Player("Bob", new BigDecimal("500")); - assertThrows(IllegalStateException.class, () -> - sale.commit(otherPlayer) - ); - } - - @Test - void testConstructorWeekZeroThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Sale(share, 0) - ); - } - - @Test - void testConstructorNegativeWeekThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Sale(share, -3) - ); - } + private Stock stock; + private Share share; + private Sale sale; + private Player player; + + @BeforeEach + void setUp() { + stock = new Stock("AAPL", "Apple", new BigDecimal("100")); + share = new Share(stock, new BigDecimal("20"), new BigDecimal("80")); + sale = new Sale(share, 1); + player = new Player("Jane", new BigDecimal("500")); + player.getPortfolio().addShare(share); + } + + // ---- Positive tests ---- + + /** + * Tests that committing a sale adds money to player's balance. + */ + @Test + void testCommitAddsMoney() { + BigDecimal before = player.getMoney(); + sale.commit(player); + assertTrue(player.getMoney().compareTo(before) > 0); + } + + /** + * Tests that committing a sale removes the share from player's portfolio. + */ + @Test + void testCommitRemovesShareFromPortfolio() { + sale.commit(player); + assertFalse(player.getPortfolio().contains(share)); + } + + /** + * Tests that committed sale is recorded in transaction archive. + */ + @Test + void testCommitRecordedInArchive() { + sale.commit(player); + assertTrue(player.getTransactionArchive().getAllTransactions().contains(sale)); + } + + /** + * Tests that committing a sale sets the committed flag. + */ + @Test + void testCommitSetsCommittedFlag() { + sale.commit(player); + assertTrue(sale.isCommitted()); + } + + /** + * Tests that getShare returns the correct share object. + */ + @Test + void testGetShareReturnsCorrectShare() { + assertEquals(share, sale.getShare()); + } + + /** + * Tests that getWeek returns the sale week. + */ + @Test + void testGetWeekReturnsCorrectWeek() { + assertEquals(1, sale.getWeek()); + } + + // ---- Negative tests ---- + + /** + * Tests that commit throws when given null player. + */ + @Test + void testCommitNullPlayerThrows() { + assertThrows(IllegalArgumentException.class, () -> + sale.commit(null) + ); + } + + /** + * Tests that commit throws when sale was already committed. + */ + @Test + void testCommitAlreadyCommittedThrows() { + sale.commit(player); + assertThrows(IllegalStateException.class, () -> + sale.commit(player) + ); + } + + /** + * Tests that commit throws when share is not in player's portfolio. + */ + @Test + void testCommitShareNotInPortfolioThrows() { + Player otherPlayer = new Player("Bob", new BigDecimal("500")); + assertThrows(IllegalStateException.class, () -> + sale.commit(otherPlayer) + ); + } + + /** + * Tests that Sale constructor throws when given zero week. + */ + @Test + void testConstructorWeekZeroThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Sale(share, 0) + ); + } + + /** + * Tests that Sale constructor throws when given negative week. + */ + @Test + void testConstructorNegativeWeekThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Sale(share, -3) + ); + } } + diff --git a/src/test/java/ShareTest.java b/src/test/java/ShareTest.java index fbc018d..af8d2cc 100644 --- a/src/test/java/ShareTest.java +++ b/src/test/java/ShareTest.java @@ -5,92 +5,129 @@ import Model.Share; import Model.Stock; - import java.math.BigDecimal; +/** + * Unit tests for the Share class. + * Tests share creation, property access, and error handling. + */ public class ShareTest { - private Stock stock; - - @BeforeEach - void setUp() { - stock = new Stock("AAPL", "Apple", new BigDecimal("150")); - } - - // ---- Positive tests ---- - - @Test - void testShareConstructorNotNull() { - Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); - assertNotNull(share); - } - - @Test - void testGetStock() { - Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); - assertEquals(stock, share.getStock()); - } - - @Test - void testGetQuantity() { - Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); - assertEquals(new BigDecimal("10"), share.getQuantity()); - } - - @Test - void testGetPurchasePrice() { - Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); - assertEquals(new BigDecimal("140"), share.getPurchasePrice()); - } - - @Test - void testPurchasePriceZeroAllowed() { - // buying at price 0 is an edge case but not invalid - Share share = new Share(stock, new BigDecimal("5"), BigDecimal.ZERO); - assertEquals(BigDecimal.ZERO, share.getPurchasePrice()); - } - - // ---- Negative tests ---- - - @Test - void testNullStockThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Share(null, new BigDecimal("10"), new BigDecimal("100")) - ); - } - - @Test - void testNullQuantityThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Share(stock, null, new BigDecimal("100")) - ); - } - - @Test - void testZeroQuantityThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Share(stock, BigDecimal.ZERO, new BigDecimal("100")) - ); - } - - @Test - void testNegativeQuantityThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Share(stock, new BigDecimal("-5"), new BigDecimal("100")) - ); - } - - @Test - void testNullPurchasePriceThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Share(stock, new BigDecimal("10"), null) - ); - } - - @Test - void testNegativePurchasePriceThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Share(stock, new BigDecimal("10"), new BigDecimal("-50")) - ); - } + private Stock stock; + + @BeforeEach + void setUp() { + stock = new Stock("AAPL", "Apple", new BigDecimal("150")); + } + + // ---- Positive tests ---- + + /** + * Tests that Share constructor creates a non-null object. + */ + @Test + void testShareConstructorNotNull() { + Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); + assertNotNull(share); + } + + /** + * Tests that getStock returns the correct stock object. + */ + @Test + void testGetStock() { + Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); + assertEquals(stock, share.getStock()); + } + + /** + * Tests that getQuantity returns the correct quantity. + */ + @Test + void testGetQuantity() { + Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); + assertEquals(new BigDecimal("10"), share.getQuantity()); + } + + /** + * Tests that getPurchasePrice returns the correct purchase price. + */ + @Test + void testGetPurchasePrice() { + Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); + assertEquals(new BigDecimal("140"), share.getPurchasePrice()); + } + + /** + * Tests that share can be created with zero purchase price. + */ + @Test + void testPurchasePriceZeroAllowed() { + // buying at price 0 is an edge case but not invalid + Share share = new Share(stock, new BigDecimal("5"), BigDecimal.ZERO); + assertEquals(BigDecimal.ZERO, share.getPurchasePrice()); + } + + // ---- Negative tests ---- + + /** + * Tests that Share constructor throws when given null stock. + */ + @Test + void testNullStockThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Share(null, new BigDecimal("10"), new BigDecimal("100")) + ); + } + + /** + * Tests that Share constructor throws when given null quantity. + */ + @Test + void testNullQuantityThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Share(stock, null, new BigDecimal("100")) + ); + } + + /** + * Tests that Share constructor throws when given zero quantity. + */ + @Test + void testZeroQuantityThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Share(stock, BigDecimal.ZERO, new BigDecimal("100")) + ); + } + + /** + * Tests that Share constructor throws when given negative quantity. + */ + @Test + void testNegativeQuantityThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Share(stock, new BigDecimal("-5"), new BigDecimal("100")) + ); + } + + /** + * Tests that Share constructor throws when given null purchase price. + */ + @Test + void testNullPurchasePriceThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Share(stock, new BigDecimal("10"), null) + ); + } + + /** + * Tests that Share constructor throws when given negative purchase price. + */ + @Test + void testNegativePurchasePriceThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Share(stock, new BigDecimal("10"), new BigDecimal("-50")) + ); + } } + diff --git a/src/test/java/StockTest.java b/src/test/java/StockTest.java index 6a6a477..5addd52 100644 --- a/src/test/java/StockTest.java +++ b/src/test/java/StockTest.java @@ -4,161 +4,231 @@ import Model.Stock; import static org.junit.jupiter.api.Assertions.*; - import java.math.BigDecimal; import java.util.List; +/** + * Unit tests for the Stock class. + * Tests stock creation, price tracking, and error handling. + */ public class StockTest { - private Stock stock; - - @BeforeEach - void setUp() { - stock = new Stock("AAPL", "Apple", new BigDecimal("1000")); - } - - // ---- Positive tests ---- - - @Test - void testGetSymbol() { - assertEquals("AAPL", stock.getSymbol()); - } - - @Test - void testGetCompany() { - assertEquals("Apple", stock.getCompany()); - } - - @Test - void testGetSalesPrice() { - assertEquals(new BigDecimal("1000"), stock.getSalesPrice()); - } - - @Test - void testGetSalesPriceZero() { - Stock zeroStock = new Stock("ZERO", "ZeroCorp", new BigDecimal("0")); - assertEquals(new BigDecimal("0"), zeroStock.getSalesPrice()); - } - - @Test - void testAddNewSalesPrice() { - stock.addNewSalesPrice(new BigDecimal("1200")); - assertEquals(new BigDecimal("1200"), stock.getSalesPrice()); - } - - @Test - void testAddNewSalesPriceZero() { - stock.addNewSalesPrice(new BigDecimal("0")); - assertEquals(new BigDecimal("0"), stock.getSalesPrice()); - } - - @Test - void testGetHistoricalPricesInitial() { - List history = stock.getHistoricalPrices(); - assertEquals(1, history.size()); - assertEquals(new BigDecimal("1000"), history.get(0)); - } - - @Test - void testGetHistoricalPricesAfterUpdates() { - stock.addNewSalesPrice(new BigDecimal("1100")); - stock.addNewSalesPrice(new BigDecimal("1200")); - assertEquals(3, stock.getHistoricalPrices().size()); - } - - @Test - void testGetHighestPrice() { - stock.addNewSalesPrice(new BigDecimal("1500")); - stock.addNewSalesPrice(new BigDecimal("800")); - assertEquals(new BigDecimal("1500"), stock.getHighestPrice()); - } - - @Test - void testGetLowestPrice() { - stock.addNewSalesPrice(new BigDecimal("1500")); - stock.addNewSalesPrice(new BigDecimal("800")); - assertEquals(new BigDecimal("800"), stock.getLowestPrice()); - } - - @Test - void testGetLatestPriceChangePositive() { - stock.addNewSalesPrice(new BigDecimal("1100")); - assertEquals(new BigDecimal("100"), stock.getLatestPriceChange()); - } - - @Test - void testGetLatestPriceChangeNegative() { - stock.addNewSalesPrice(new BigDecimal("900")); - assertEquals(new BigDecimal("-100"), stock.getLatestPriceChange()); - } - - @Test - void testGetLatestPriceChangeOnlyOnePrice() { - assertEquals(BigDecimal.ZERO, stock.getLatestPriceChange()); - } - - @Test - void testHistoricalPricesIsDefensiveCopy() { - List history = stock.getHistoricalPrices(); - history.add(new BigDecimal("9999")); - assertEquals(1, stock.getHistoricalPrices().size()); - } - - // ---- Negative tests ---- - - @Test - void testNullSymbolThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Stock(null, "Apple", new BigDecimal("100")) - ); - } - - @Test - void testBlankSymbolThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Stock(" ", "Apple", new BigDecimal("100")) - ); - } - - @Test - void testNullCompanyThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Stock("AAPL", null, new BigDecimal("100")) - ); - } - - @Test - void testBlankCompanyThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Stock("AAPL", " ", new BigDecimal("100")) - ); - } - - @Test - void testNullInitialPriceThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Stock("AAPL", "Apple", null) - ); - } - - @Test - void testNegativeInitialPriceThrows() { - assertThrows(IllegalArgumentException.class, () -> - new Stock("AAPL", "Apple", new BigDecimal("-1")) - ); - } - - @Test - void testAddNullPriceThrows() { - assertThrows(IllegalArgumentException.class, () -> - stock.addNewSalesPrice(null) - ); - } - - @Test - void testAddNegativePriceThrows() { - assertThrows(IllegalArgumentException.class, () -> - stock.addNewSalesPrice(new BigDecimal("-10")) - ); - } + private Stock stock; + + @BeforeEach + void setUp() { + stock = new Stock("AAPL", "Apple", new BigDecimal("1000")); + } + + // ---- Positive tests ---- + + /** + * Tests that getSymbol returns the correct stock symbol. + */ + @Test + void testGetSymbol() { + assertEquals("AAPL", stock.getSymbol()); + } + + /** + * Tests that getCompany returns the correct company name. + */ + @Test + void testGetCompany() { + assertEquals("Apple", stock.getCompany()); + } + + /** + * Tests that getSalesPrice returns the current stock price. + */ + @Test + void testGetSalesPrice() { + assertEquals(new BigDecimal("1000"), stock.getSalesPrice()); + } + + /** + * Tests that stock can have zero sales price. + */ + @Test + void testGetSalesPriceZero() { + Stock zeroStock = new Stock("ZERO", "ZeroCorp", new BigDecimal("0")); + assertEquals(new BigDecimal("0"), zeroStock.getSalesPrice()); + } + + /** + * Tests that adding a new sales price updates the current price. + */ + @Test + void testAddNewSalesPrice() { + stock.addNewSalesPrice(new BigDecimal("1200")); + assertEquals(new BigDecimal("1200"), stock.getSalesPrice()); + } + + /** + * Tests that zero price can be added to price history. + */ + @Test + void testAddNewSalesPriceZero() { + stock.addNewSalesPrice(new BigDecimal("0")); + assertEquals(new BigDecimal("0"), stock.getSalesPrice()); + } + + /** + * Tests that historical prices initially contain only the initial price. + */ + @Test + void testGetHistoricalPricesInitial() { + List history = stock.getHistoricalPrices(); + assertEquals(1, history.size()); + assertEquals(new BigDecimal("1000"), history.get(0)); + } + + /** + * Tests that historical prices accumulates all price changes. + */ + @Test + void testGetHistoricalPricesAfterUpdates() { + stock.addNewSalesPrice(new BigDecimal("1100")); + stock.addNewSalesPrice(new BigDecimal("1200")); + assertEquals(3, stock.getHistoricalPrices().size()); + } + + /** + * Tests that getHighestPrice returns the maximum price from history. + */ + @Test + void testGetHighestPrice() { + stock.addNewSalesPrice(new BigDecimal("1500")); + stock.addNewSalesPrice(new BigDecimal("800")); + assertEquals(new BigDecimal("1500"), stock.getHighestPrice()); + } + + /** + * Tests that getLowestPrice returns the minimum price from history. + */ + @Test + void testGetLowestPrice() { + stock.addNewSalesPrice(new BigDecimal("1500")); + stock.addNewSalesPrice(new BigDecimal("800")); + assertEquals(new BigDecimal("800"), stock.getLowestPrice()); + } + + /** + * Tests that getLatestPriceChange returns positive change for price increase. + */ + @Test + void testGetLatestPriceChangePositive() { + stock.addNewSalesPrice(new BigDecimal("1100")); + assertEquals(new BigDecimal("100"), stock.getLatestPriceChange()); + } + + /** + * Tests that getLatestPriceChange returns negative change for price decrease. + */ + @Test + void testGetLatestPriceChangeNegative() { + stock.addNewSalesPrice(new BigDecimal("900")); + assertEquals(new BigDecimal("-100"), stock.getLatestPriceChange()); + } + + /** + * Tests that price change is zero when only one price exists. + */ + @Test + void testGetLatestPriceChangeOnlyOnePrice() { + assertEquals(BigDecimal.ZERO, stock.getLatestPriceChange()); + } + + /** + * Tests that getHistoricalPrices returns a defensive copy. + */ + @Test + void testHistoricalPricesIsDefensiveCopy() { + List history = stock.getHistoricalPrices(); + history.add(new BigDecimal("9999")); + assertEquals(1, stock.getHistoricalPrices().size()); + } + + // ---- Negative tests ---- + + /** + * Tests that Stock constructor throws when given null symbol. + */ + @Test + void testNullSymbolThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Stock(null, "Apple", new BigDecimal("100")) + ); + } + + /** + * Tests that Stock constructor throws when given blank symbol. + */ + @Test + void testBlankSymbolThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Stock(" ", "Apple", new BigDecimal("100")) + ); + } + + /** + * Tests that Stock constructor throws when given null company name. + */ + @Test + void testNullCompanyThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Stock("AAPL", null, new BigDecimal("100")) + ); + } + + /** + * Tests that Stock constructor throws when given blank company name. + */ + @Test + void testBlankCompanyThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Stock("AAPL", " ", new BigDecimal("100")) + ); + } + + /** + * Tests that Stock constructor throws when given null initial price. + */ + @Test + void testNullInitialPriceThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Stock("AAPL", "Apple", null) + ); + } + + /** + * Tests that Stock constructor throws when given negative initial price. + */ + @Test + void testNegativeInitialPriceThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Stock("AAPL", "Apple", new BigDecimal("-1")) + ); + } + + /** + * Tests that addNewSalesPrice throws when given null price. + */ + @Test + void testAddNullPriceThrows() { + assertThrows(IllegalArgumentException.class, () -> + stock.addNewSalesPrice(null) + ); + } + + /** + * Tests that addNewSalesPrice throws when given negative price. + */ + @Test + void testAddNegativePriceThrows() { + assertThrows(IllegalArgumentException.class, () -> + stock.addNewSalesPrice(new BigDecimal("-10")) + ); + } } + diff --git a/src/test/java/TransactionArchiveTest.java b/src/test/java/TransactionArchiveTest.java index 14ff009..c3a1fa1 100644 --- a/src/test/java/TransactionArchiveTest.java +++ b/src/test/java/TransactionArchiveTest.java @@ -1,148 +1,196 @@ import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - import Model.Player; import Model.Purchase; import Model.Sale; import Model.Share; import Model.Stock; import Model.TransactionArchive; - import java.math.BigDecimal; import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +/** + * Unit tests for the TransactionArchive class. + * Tests transaction recording, retrieval, filtering, and error handling. + */ public class TransactionArchiveTest { - private TransactionArchive archive; - private Stock stock; - private Share share; - private Purchase purchase; - private Sale sale; - - @BeforeEach - void setUp() { - archive = new TransactionArchive(); - stock = new Stock("AAPL", "Apple", new BigDecimal("100")); - share = new Share(stock, new BigDecimal("10"), new BigDecimal("90")); - purchase = new Purchase(share, 1); - sale = new Sale(share, 2); - } - - // ---- Positive tests ---- - - @Test - void testIsEmptyOnCreation() { - assertTrue(archive.isEmpty()); - } - - @Test - void testIsEmptyAfterAdd() { - archive.add(purchase); - assertFalse(archive.isEmpty()); - } - - @Test - void testAddReturnsTrue() { - assertTrue(archive.add(purchase)); - } - - @Test - void testGetAllTransactions() { - archive.add(purchase); - archive.add(sale); - assertEquals(2, archive.getAllTransactions().size()); - } - - @Test - void testGetAllTransactionsIsDefensiveCopy() { - archive.add(purchase); - List copy = archive.getAllTransactions(); - copy.clear(); - assertEquals(1, archive.getAllTransactions().size()); - } - - @Test - void testGetTransactionsByWeek() { - archive.add(purchase); // week 1 - archive.add(sale); // week 2 - - List week1 = archive.getTransactions(1); - assertEquals(1, week1.size()); - assertTrue(week1.contains(purchase)); - } - - @Test - void testGetTransactionsByWeekNoMatch() { - archive.add(purchase); // week 1 - assertTrue(archive.getTransactions(99).isEmpty()); - } - - @Test - void testGetPurchaseByWeek() { - archive.add(purchase); // week 1 - archive.add(sale); // week 2 - - List purchases = archive.getPurchase(1); - assertEquals(1, purchases.size()); - assertTrue(purchases.contains(purchase)); - } - - @Test - void testGetPurchaseByWeekExcludesSales() { - // Add a sale at week 1 — should not appear in getPurchase(1) - Sale saleWeek1 = new Sale(share, 1); - archive.add(saleWeek1); - - assertTrue(archive.getPurchase(1).isEmpty()); - } - - @Test - void testGetSaleByWeek() { - archive.add(purchase); // week 1 - archive.add(sale); // week 2 - - List sales = archive.getSale(2); - assertEquals(1, sales.size()); - assertTrue(sales.contains(sale)); - } - - @Test - void testGetSaleByWeekExcludesPurchases() { - // Add a purchase at week 2 — should not appear in getSale(2) - Purchase purchaseWeek2 = new Purchase(share, 2); - archive.add(purchaseWeek2); - - assertTrue(archive.getSale(2).isEmpty()); - } - - @Test - void testCountDistinctWeeksEmpty() { - assertEquals(0, archive.countDistinctWeeks()); - } - - @Test - void testCountDistinctWeeksMultipleTransactionsSameWeek() { - Purchase purchase2 = new Purchase(share, 1); - archive.add(purchase); - archive.add(purchase2); - assertEquals(1, archive.countDistinctWeeks()); - } - - @Test - void testCountDistinctWeeksAcrossWeeks() { - archive.add(purchase); // week 1 - archive.add(sale); // week 2 - assertEquals(2, archive.countDistinctWeeks()); - } - - // ---- Negative tests ---- - - @Test - void testAddNullThrows() { - assertThrows(IllegalArgumentException.class, () -> - archive.add(null) - ); - } + private TransactionArchive archive; + private Stock stock; + private Share share; + private Purchase purchase; + private Sale sale; + + @BeforeEach + void setUp() { + archive = new TransactionArchive(); + stock = new Stock("AAPL", "Apple", new BigDecimal("100")); + share = new Share(stock, new BigDecimal("10"), new BigDecimal("90")); + purchase = new Purchase(share, 1); + sale = new Sale(share, 2); + } + + // ---- Positive tests ---- + + /** + * Tests that archive is empty immediately after creation. + */ + @Test + void testIsEmptyOnCreation() { + assertTrue(archive.isEmpty()); + } + + /** + * Tests that isEmpty returns false after adding a transaction. + */ + @Test + void testIsEmptyAfterAdd() { + archive.add(purchase); + assertFalse(archive.isEmpty()); + } + + /** + * Tests that add method returns true for successful addition. + */ + @Test + void testAddReturnsTrue() { + assertTrue(archive.add(purchase)); + } + + /** + * Tests that getAllTransactions returns all stored transactions. + */ + @Test + void testGetAllTransactions() { + archive.add(purchase); + archive.add(sale); + assertEquals(2, archive.getAllTransactions().size()); + } + + /** + * Tests that getAllTransactions returns a defensive copy. + */ + @Test + void testGetAllTransactionsIsDefensiveCopy() { + archive.add(purchase); + List copy = archive.getAllTransactions(); + copy.clear(); + assertEquals(1, archive.getAllTransactions().size()); + } + + /** + * Tests that getTransactions filters transactions by week number. + */ + @Test + void testGetTransactionsByWeek() { + archive.add(purchase); // week 1 + archive.add(sale); // week 2 + + List week1 = archive.getTransactions(1); + assertEquals(1, week1.size()); + assertTrue(week1.contains(purchase)); + } + + /** + * Tests that getTransactions returns empty list for non-existent week. + */ + @Test + void testGetTransactionsByWeekNoMatch() { + archive.add(purchase); // week 1 + assertTrue(archive.getTransactions(99).isEmpty()); + } + + /** + * Tests that getPurchase returns only purchase transactions for week. + */ + @Test + void testGetPurchaseByWeek() { + archive.add(purchase); // week 1 + archive.add(sale); // week 2 + + List purchases = archive.getPurchase(1); + assertEquals(1, purchases.size()); + assertTrue(purchases.contains(purchase)); + } + + /** + * Tests that getPurchase does not return sale transactions. + */ + @Test + void testGetPurchaseByWeekExcludesSales() { + // Add a sale at week 1 — should not appear in getPurchase(1) + Sale saleWeek1 = new Sale(share, 1); + archive.add(saleWeek1); + + assertTrue(archive.getPurchase(1).isEmpty()); + } + + /** + * Tests that getSale returns only sale transactions for week. + */ + @Test + void testGetSaleByWeek() { + archive.add(purchase); // week 1 + archive.add(sale); // week 2 + + List sales = archive.getSale(2); + assertEquals(1, sales.size()); + assertTrue(sales.contains(sale)); + } + + /** + * Tests that getSale does not return purchase transactions. + */ + @Test + void testGetSaleByWeekExcludesPurchases() { + // Add a purchase at week 2 — should not appear in getSale(2) + Purchase purchaseWeek2 = new Purchase(share, 2); + archive.add(purchaseWeek2); + + assertTrue(archive.getSale(2).isEmpty()); + } + + /** + * Tests that countDistinctWeeks returns zero for empty archive. + */ + @Test + void testCountDistinctWeeksEmpty() { + assertEquals(0, archive.countDistinctWeeks()); + } + + /** + * Tests that countDistinctWeeks counts only distinct weeks. + */ + @Test + void testCountDistinctWeeksMultipleTransactionsSameWeek() { + Purchase purchase2 = new Purchase(share, 1); + archive.add(purchase); + archive.add(purchase2); + assertEquals(1, archive.countDistinctWeeks()); + } + + /** + * Tests that countDistinctWeeks correctly counts across multiple weeks. + */ + @Test + void testCountDistinctWeeksAcrossWeeks() { + archive.add(purchase); // week 1 + archive.add(sale); // week 2 + assertEquals(2, archive.countDistinctWeeks()); + } + + // ---- Negative tests ---- + + /** + * Tests that add throws when given null transaction. + */ + @Test + void testAddNullThrows() { + assertThrows(IllegalArgumentException.class, () -> + archive.add(null) + ); + } } + diff --git a/target/test-classes/ExchangeTest.class b/target/test-classes/ExchangeTest.class index 5b02574e2280eca7a62f88929280c7d347ae094e..c9e32fd89c87350a4ead3475fed0f9295f726766 100644 GIT binary patch literal 10733 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 literal 10397 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 BN(=x1 diff --git a/target/test-classes/ShareTest.class b/target/test-classes/ShareTest.class index 65bc1e8252dc9cc05db520ac079a267668bb9a4f..959b07a62f55678b3a56ea82ddb5792a1d317529 100644 GIT binary patch literal 4255 zcmbVO3s)0I7`=l)LRb(Zq9}p_mVm{8l~#?~21KQefPyI6)-GX47B<Q7v_rC$)^K2hl=6!y`i>G$AZVcQWjXB#N{%mZ@b^vHrOb z0@x)+v@x9a8L@ee^QMfEkQ%TD?E&mnun!#!4Kr!gu3vMKClaq&39?=q#>M z3=ZM|L(T9|5ZyxlF_OO@y+Yg}hEtJ}$^I(*--=cCAre5pf&m<6IAEBm=yHJ)PS2cX zaWkssv}in^=cc6@**r;A?O@WdCJVY=mOW?F?CkBwpn@ZaG6YlHvNwxid!)Zate_uO za1_rlC=T%Wt%9l(PoJD~7gx%83DbyzXYm~6jF>JJ)U2ghcNq2%nV$(_B6}wkoD@@9 zXv%cKOs3U5pEk85<@yu@8)+beoKf&RULb#6AHO`s(B_X8z$im^(Locs&Qq!$H&cZS z&syWRlRPJK7beG_RWOco3;}+dCkqy57$lMYNna{tlVVBYGxh|)Zl>(Yvk`5&Fc=a-@#EG)v)|DJX`{e?8A;agiyhc;v*{oryu8+GbWM`Bw873cBlj#`4K8woKUiKK7 z-!cnH%P>9b*X2cVO4Tbun$=MOTGTvsD+2Up_Yi+|8Z5wYl;7d9LKzYYrdj?t9(pGtSr=1W%Qleu6vrC@pJ0ha1nT18Q zcZ-3&_EB6^^}vZMDz^uB_;9epUGj}(}8^ofW*Rgk5q9-oKs1-=a6D+OQUo01bu%dQ$L zJesh#iiD_3s_;D>sf`&q`ddD7+)0k^3u<;z=lR}*VXPE#G3nfP?^o$f$;8<^IW78;FP4nUH>)5f5*59$a2q?b1fL2noO9E|oL0_dlL3vZ)<28@q zCVdr&c|DJ@e}3RM?6&c{2;XiTw268WhacjpB2_aT{y-=Fb)g#vY`QiF#-;0|1gKab z!VTi2;KiaxyyOzo!m1GqNr(Ty(+@FJ&fiD;5%kla$f3hO;PETI{M_STarvXw`Mq?I zh+{q?hDgLPeHI;+L_|FiJA6ef{Vx$>D@ep~9}y==#7Pnndjb(DPsB~TRc?V8(fx|k z>*z_)PX*#Ja@$$5z_>)*!#$3#%aD3nqUh)UMMiqBM0&VgIU9JE+=?}Uo0Mi9rRrxFlwhxQB3d%DQfuC}^ z?_Gj1H#O}W%BcXWLMJ8YjRRj!3Vm33NpZ67C52T5o|k|(CnY4lUtHj0q*?sy5SkrO z?-G$S!lcV@imdJK99<}D)ULs8awc$wa6hAWuj38+@3h|bTKB!yyI$)9uk|stz9ozh M?f!T80Y9PrUo#V+wg3PC literal 4137 zcmbtWX;%|h7=A8WLKp-=DX7H_WfKD`ts1lhM9@Y+KoqOeAzaA7BoikSlz})S0yu@A11MrBA5W+zpWqoQ zfD(r41$9Xc>uPE~JTkMu;}%0{#xmlI43)79#w^#vxNm(mOk zW7(9YCHbV5(dbAtl`<^V(u@?H*cUU*`S3!PM9_Cyv$z>n(^|NX&lx5kP~(Y60NV)J zBRhyXQW}kp#)7CPnrJ$$^8j`*Y?F>BRVxwh)8^0cxRzA)Aa;>^UEQ4_G@w!VwVPpk zOUw<@xMgao`AF+z2({QNcI;y~<+Ee$nVe-6BO#T8p-Dk0${2Q9lo%P9e)8EevvJEX zhYf2utLqHA)`@_~Ci&jt`Yv)DL>og#+US_^yi$UeWv!k&P(@SMivW zjhqOEac?&IbxKv_K7j40%Mm6KB^^Z~?{GdvIl3C^wj08iX@=u1o*{HU&e-T8BdgJi zaD!ooajV{trkm9_=60v2s7sPj4sn{0O)#S{q+SJJqaxzA#r3qf^ zrq+o}5J@o&I%#|UWS6_7md;y`24T|t=%DV+n~u55?jV*3r=wfYjvG|i{H!Ks{uXDf z9~SIX+kJ*%(I`{X7r2!$W-}o?iKhd2s-X0=)RM8t!!hX#ZB*w}G5VGjJcDPc)sn7> z;csuj*e7$~LuzVP=b5IMVJv3T5l>IsgWF>%I5@GK=HBg_|1n3o@mX(;+Q|JXwIbbN zk*YF0QH@}T>heVe2?WXT0vT-EYYI|i3*c4P_O=2;+TL<)A1Fvm+xxEVQw14m`^2?< zrNEN5FUdx&crKOVrffP+Me{ww0k>!hf>0RR0Dfd>+;}VfWys|Ld&#+C8X{Cr;+~^b zEv@Na1AUj%TbQ1Vo~3Oo2>n8fA}I7-CXE%Sq_vXUfGwz^H-OaBuC25ZyTpVNK!Xwz zXlT2K+NqlDH4ofF!&J@A6+F0tJ-=hW3m&55Ab@&0>=HCM1P2NT4%!6Mq*|QYe-F)5 zZNG^p;kOdLT(nD~j#&F0bh=bqr1SM?q*zKiGMd*vB$&T=I|>Hf7F+M(dG|t%+DXKr{}6HBSHv_`+}aX&l<0oNg%xzf=&=rQ7ggI4DuJUO z;{CSw^{8;XzvxSRl4wPjC^chuF#+7cmCJ6DbM<_JPOS$=DY}n2O(qw5Zsxd^kQ3xoD&*LS$ELX4Kb-W>0Z{i)iD_8H~LwqDxALBE8E>~aRYkY%m P2}Att_zpkdCp7#EbX!Al diff --git a/target/test-classes/StockTest.class b/target/test-classes/StockTest.class index e22183d1d0208cb1c72e53109d70a846ae53f6ee..086bf5b8c9ecf660b77db09a000cb1607902004b 100644 GIT binary patch literal 6225 zcmbVP`F9i775>K9mMjkjgzexo0S64&P$Lsa0vM9m#sQ~d2{xvXr5(#-d9bVzS^z`a zq$FLFu35S_X}Ts&w}h}ZG+mR@bV-`-J^igc{VRI<-8ZArNE+KseU3-(&Aacr-@WhN z`|e!*&!sB>cHplO)L=mfwL0ohuVL{~>3Rr ztf6s_J#MCA!+AR~6-GouZC~I1co@2dy1q;%WronCp;g76CpqD19B ztRiGJU{7a^^o)>QujB3LrWr!kNSV3)Su0^`SXLxl>69>HJcuKM2lj=rQA7BMnY9P( zY$l9N8nm7;Zjy7lQO+rZTXbx}Rz`<0KEBsH;aAY+a<9a9*!Jq!jvX47lGbjEt|luZ zrMS`VXpvkYZ>3^!uJ<YyU_CfLa9X!R(p`or( z*}PlF5bmS|HA-L&`47zXWEgQVj!_uKUK!maqy5~p#j*E*j$!fUB1Jnn$w*P0#TN&4 zjLKRQ*W&hx;!+sz9@XVT@CFEC0mG2^~pH5{3s-kv_#}ITFHA4J%7|HIz!3Nh8&lO%|rjbbjz;!pulK zMp+0_I;N4<5He4ii9((p?&jG92l7NAosfO8K_zR9rA+3_N@R2#L)HuKk;$xmf`>fp z5AJ~!Bfs}80Q*l*Au4rIXh=m_x%gNhk6L-u$)g@KV#_HFTRP)jp7*{LW1>94nK0f< z_wsPUxL-p}_r@^Z&);^YUhM$OAA=)qrOmyC=`k}qBCHtCahuUSYGf^$yDw|=lNR03 z=p!Rl$IKthXlOf7Naw9-bJWUN+|rj$+j+w+5-VqMQ@=T3XU#z)G1<#}m*Cn}Dp1$W zvPp^^#hYAlw{mD$Qrz#W87)=sq&Tn;p5l*kDI;5C`l_nIvY^+_?&U6OWf>S^p|lLv z))4)~0x`of_9Wg~?%J75(YXK>`X^ zqJ{uvI~A-7C&w#gi>p$`^w_we{1hzH(ZdVTD`>e8p5Zx`DxX(4G_0t=Dkma1Ykd;4 z+ai*FUcoLADZj>QkBY={zsAb5s1&F=L=m{Y0>OXCvK|?>3)zHuhb2|2$*YPR#6?WE ze%sFH@>wIZ$IMUK)t9pC1x4zJ4WmpYzRvtP;VH67njlD~=IxAhO-!dHU8GEI}hy#9Ms@JK4lqb!b5!OjsQM@4{`>4h^sYf z^DczdTHCtI2px_#L>n(-;o<0_^H_2o%`f7b5}==WOYG_pbCjyh)O!v$d*dKG9nH64F>c2y z^!aS<2(k&BW64>l1+*3hdpEFku zk~N3OU1g!}a4^YXf*g|Agh?NV&4C;~_kVH_J`RVG01h@eWXRzdIb`SM@Oh6zb?CbX zq3k~7={#Jf(+T|<>O93zJnhpL9Qt3FL*G3ouf8vO`nJ2pwdr}h z+woqxB~HnHm|w4t_)wbyPwq<|>S;!gBvh9Jxh=k4xhp~X7h%@mP_4V*4_kjuJ<1$; zjQ7Oj=)@CBu~W0UT*YDDX_Yx8Z>n?Z%O0t1uBm#|x`Zh(mF+{Nba4!Q5{-D;XQ&=% z=##;ixnRs^i22;Sm`?>`=7TX`Am+JwF~1UwSqR2_iI^|Xi}`dg=J8<6SBQCaUd*os zW1a}ce4UtY&Wrgq53|%iSnDp~WH9L8SeXCe0}bvUzCH`+>0r=*67=75gD$R--k6bj zhUwDl7F|`xRK~o=N&ZrpIz8FHe4tC*5d9_r?x8 zDyWEb^)A!~b|CWCTGr>ij||!8sZ7#*ZQ~m#5sJu{f{U_-eW(026;ZUH-LIDI=2yoo z93Jq>a1hFsG8BHvxG3GOB`ppIZBCH0Wgz@G3Z(LY+xE(h5JctKG8BGr1yRxF0ePwf z!j8o`$>;F`y?KE3^4ENK7BBMO8C~#3m%Y&yZ}ekt^fPbtiZ{B-(VN7LFrfZ`KjANE F`!8O2Q-uHk literal 6116 zcmb_f`Fm8=8GdgPGLzvFV3HuQN+SeGf?*N}3QVvjAwqOWkc3cB+ulrWCKo1i$60`2 zUD~?TYHh2vt(LZ0YxmVE2()(Jtljs0-~Nd{eb2df?!7Z}Nbq?&KTOVi=bZO@zx8~1 z>A&Y*1h5nT386+q!+60?9-T1rg%IjAtT!vg4fXw2#wuK^p|+!QQbXMkcO$69QXL_LH7rk98FREaH)ZA~jH$G#A)2t0Mtai7 zS>m@VtSih~c@0Yw9uZ8xMmmhkc)Yi#rze6|tPzI2T0=`m!uQg6A!lV$@y^Kzns9|Uu~x%Q#fj=W zj-)~*ks6?(UB?pC)9D39X17@wpP!qu(;6vinL9@dOe zag2HE`lgba@WyHtvf!d&jmvwF#hRo2Mp}9yKf)ZhjI>ai(6JYz8k*>h%44jsl23{S zD;-M^CcYH=us?*G7zUZO-j0rOOwgRXb&|EoA|4SbF)4z5kfqTvGSWFIE+5iy7&r3- zHHzdP2_};pWep)#F*(oE9ZnK>dRWe@$_sx!TPl9nP2SmlbxmALjCB|A&AiWB0elwI>jbpnPL+Z zV8;ozU06fE*L6#~+b=|6ER&ue%b3y;4Qyv`)X|SSL~vBCujKK{92?2+GH1+8p6Z9} z?0gt^Q}cFyM5?@5$6Ig@Ik6%t`edmc9Ck}JT}XCKdpa6enR-&aS{XJTl{68 zzuX$er+KHl*B4#wFRuyX^GsmY61j}q#az<7-V(!0qc@*y5|r5I25q~LFXW8uUb8T3 zPv;|e9%n*0U6#jI=9qodj3uOjJZQ`qNzo^DynwSDPUd`(hL?lc=iYR{_ZXRJK8o!L z+df*%2H#nC013RjWfv#dY=g%ye$!F8@ZDg-yU0TZ3!B4Oe0e>dTY+$dS^i}mPa;gg zmnd*$U)S*wDf^l)`;Lx}N!ho3*~>aUE@j{MWk1pJ2`T%rFZ-pAPfFP@eA(}Ge1@_R ze(THrtmCs%_9x0%|09`X4}NRJDq&7KDw?5h>n zYxx?l3t%^Ugm);hrx-ioy(^`cufC%hjUPorF0qs)R0Xh8?ob5Iw4U*#aV z8e15V0XOi2O5KyFlXkTWbEK*v>OGBNZ>_S^(cI0n*+ffY0bAQWTSqQZpRlu}PpmJj zZwvMHQQuajz8jSKPS8ivw+`%$iMowvF{ZI!9*`D_T;5oF7O$Wb^ zj_w#>=?|g@LjjlcdLc+E2^?_=^mz)WD+tUuzomo3oIgDTnZU}Jb z^Ee#+KX4Fy91dv(hf#7EBZqzDaFddQt>jRh`febk-C0j(u1u$s`Uj|UlBswwpwCbJ z;zjB^7Vz_l1@+xbeYa5GtxA0-mHPVK=GyoyZg;#_Zi&;f4I)kjP#e9--Kj*qo!KJ_ zs?&kIF|kp)>n_qii#OHaV4b@WOxs{h%@8)lcVd=Z%aV$np4I6p4)cAhtSKRJYU*Ak zsU5DVdi1_?cq>@S4;}Q&#WB>zQse@L>YkzRTpaUVYRqHAoL6E#vN-0WYRo%`dAAbt z-HT&BrpCO7nD;9&-?KR8d)1f^67yjt=KB`Md|ZwB2r(a1Vm_h7^yi2By3XPKYS72o zm`?>j{rTa87XkVqHRzKBeYzTSMMTV|%*Ll!BI0HJupFfN_`-`1r-!CPnma~`4#P3Z z&r@eW%Su$17_m6hR$*;*v0CJ`@XW$s#{*y-hO5BJh(%j8uk|U2-5J2*5M6~;POK&R z%tB!I2EaIdSAlhNvK60YrO8uMFnTBe!g0L{q>_;{zJ5yW_eVXj8ZqZrfo-8gBcle< z-!x7vm4SOg0yvX(gVS7A)QEo%s-WWT3%pW5NiThloD`5x1t3?t{i+s@;%D(ive{4g zbDVL`<3)T$uD*(I;G1&wEqoW>ldG5T1N=~~euSUmXL9v({0hI8tKZ=F_=8;i5r4s7 R@i*d%f3*INf8yV0{U0rAvt