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 5b02574..c9e32fd 100644 Binary files a/target/test-classes/ExchangeTest.class and b/target/test-classes/ExchangeTest.class differ diff --git a/target/test-classes/PortfolioTest.class b/target/test-classes/PortfolioTest.class index 45ae586..6b62be5 100644 Binary files a/target/test-classes/PortfolioTest.class and b/target/test-classes/PortfolioTest.class differ diff --git a/target/test-classes/ShareTest.class b/target/test-classes/ShareTest.class index 65bc1e8..959b07a 100644 Binary files a/target/test-classes/ShareTest.class and b/target/test-classes/ShareTest.class differ diff --git a/target/test-classes/StockTest.class b/target/test-classes/StockTest.class index e22183d..086bf5b 100644 Binary files a/target/test-classes/StockTest.class and b/target/test-classes/StockTest.class differ