diff --git a/src/main/java/Controller/StockFileHandler.java b/src/main/java/Controller/StockFileHandler.java index 8874232..b64f9c0 100644 --- a/src/main/java/Controller/StockFileHandler.java +++ b/src/main/java/Controller/StockFileHandler.java @@ -11,43 +11,27 @@ public class StockFileHandler { // lesing public List loadStocksFromFile(String filename) throws IOException { - List stocks = new ArrayList<>(); - List lines = Files.readAllLines(Paths.get(filename)); - - for (String line : lines) { - line = line.trim(); - if (line.isEmpty() || line.startsWith("#")) { - continue; - } - - String[] parts = line.split(","); - if (parts.length == 3) { - String symbol = parts[0]; - String name = parts[1]; - BigDecimal price = new BigDecimal(parts[2]); - - stocks.add(new Stock(symbol, name, price)); - } - } - return stocks; // returnerer listen + return Files.readAllLines(Paths.get(filename)).stream() + .map(String::trim) + .filter(line -> !line.isEmpty() && !line.startsWith("#")) + .map(line -> line.split(",")) + .filter(parts -> parts.length == 3) + .map(parts -> new Stock(parts[0], parts[1], new BigDecimal(parts[2]))) + .toList(); } // lagring public void saveStocksToFile(String filename, List stocks) throws IOException { List lines = new ArrayList<>(); - - // header lines.add("# Ticker,Name,Price"); - - for (Stock stock : stocks) { - // formaterer hver aksje som "SYMBOL,NAME,PRICE" - String line = String.format("%s,%s,%s", + + lines.addAll(stocks.stream() + .map(stock -> String.format("%s,%s,%s", stock.getSymbol(), stock.getCompany(), - stock.getSalesPrice().toString()); - lines.add(line); - } - + stock.getSalesPrice().toString())) + .toList()); + Files.write(Paths.get(filename), lines); } } \ No newline at end of file diff --git a/src/main/java/Model/Portfolio.java b/src/main/java/Model/Portfolio.java index 949bea3..ba050e7 100644 --- a/src/main/java/Model/Portfolio.java +++ b/src/main/java/Model/Portfolio.java @@ -33,13 +33,9 @@ public List getShares(String symbol) { if (symbol == null || symbol.isBlank()) { throw new IllegalArgumentException("Symbol cannot be null or blank"); } - List result = new ArrayList<>(); - for (Share share : shares) { - if (share.getStock().getSymbol().equals(symbol)) { - result.add(share); - } - } - return result; + return shares.stream() + .filter(share -> share.getStock().getSymbol().equals(symbol)) + .toList(); } public boolean contains(Share share) { @@ -50,12 +46,9 @@ public boolean contains(Share share) { } public BigDecimal getNetWorth() { - BigDecimal total = BigDecimal.ZERO; - for (Share share : shares) { - SaleCalculator calc = new SaleCalculator(share); - total = total.add(calc.calculateTotal()); - } - return total; + return shares.stream() + .map(share -> new SaleCalculator(share).calculateTotal()) + .reduce(BigDecimal.ZERO, BigDecimal::add); } } diff --git a/src/test/java/ExchangeTest.java b/src/test/java/ExchangeTest.java index f80b1d9..9261bd6 100644 --- a/src/test/java/ExchangeTest.java +++ b/src/test/java/ExchangeTest.java @@ -1,8 +1,13 @@ -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import Model.Exchange; +import Model.Player; +import Model.Share; import Model.Stock; +import Model.Transaction; +import Model.Purchase; +import Model.Sale; import static org.junit.jupiter.api.Assertions.*; @@ -15,9 +20,10 @@ public class ExchangeTest { private Exchange exchange; private Stock apple; private Stock google; + private Player player; - @BeforeAll - public void setUp() { + @BeforeEach + void setUp() { apple = new Stock("AAPL", "Apple", new BigDecimal("100")); google = new Stock("GOOGL", "Google", new BigDecimal("200")); @@ -25,29 +31,235 @@ public void setUp() { stocks.add(apple); stocks.add(google); - exchange = new Exchange("ABC", stocks); + exchange = new Exchange("TestExchange", stocks); + player = new Player("Jane", new BigDecimal("500000")); } + // ---- Positive tests ---- + @Test - public void testFindStocksBySymbol() { - List result = exchange.findStocks("AAPL"); + 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 - public void testFindStocksByCompanyNames() { + void testFindStocksByCompanyName() { List result = exchange.findStocks("e"); - assertEquals(2, result.size()); } @Test - public void testFindStocksNotInList() { + 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) + ); + } } diff --git a/src/test/java/PlayerTest.java b/src/test/java/PlayerTest.java new file mode 100644 index 0000000..ac1f95a --- /dev/null +++ b/src/test/java/PlayerTest.java @@ -0,0 +1,128 @@ +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import Model.Player; + +import java.math.BigDecimal; + +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")) + ); + } +} diff --git a/src/test/java/PortfolioTest.java b/src/test/java/PortfolioTest.java index d921601..34e46aa 100644 --- a/src/test/java/PortfolioTest.java +++ b/src/test/java/PortfolioTest.java @@ -1,5 +1,6 @@ import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import Model.Portfolio; @@ -7,82 +8,148 @@ import Model.Stock; import java.math.BigDecimal; +import java.util.List; public class PortfolioTest { - @Test - void testAddSharePortfolio() { - Portfolio portfolio = new Portfolio(); + private Portfolio portfolio; + private Stock stock; + private Share share; - Stock stock = new Stock("AAPL", "Apple", new BigDecimal("150")); - Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); + @BeforeEach + void setUp() { + portfolio = new Portfolio(); + stock = new Stock("AAPL", "Apple", new BigDecimal("150")); + share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); + } - boolean result = portfolio.addShare(share); + // ---- Positive tests ---- + @Test + void testAddShare() { + boolean result = portfolio.addShare(share); assertTrue(result); assertTrue(portfolio.contains(share)); } @Test - void testRemoveSharePortfolio() { - Portfolio portfolio = new Portfolio(); - - Stock stock = new Stock("AAPL", "Apple", new BigDecimal("150")); - Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); + 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); - boolean result = portfolio.removeShare(share); + assertEquals(2, portfolio.getShares().size()); + } + @Test + void testRemoveShare() { + portfolio.addShare(share); + boolean result = portfolio.removeShare(share); assertTrue(result); assertFalse(portfolio.contains(share)); } @Test - void testGetSharesPortfolio() { - Portfolio portfolio = new Portfolio(); - - Stock stock = new Stock("AAPL", "Apple", new BigDecimal("150")); - Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); + void testRemoveShareNotInPortfolioReturnsFalse() { + boolean result = portfolio.removeShare(share); + assertFalse(result); + } + @Test + void testGetSharesReturnsAll() { portfolio.addShare(share); - - var shares = portfolio.getShares(); - + List shares = portfolio.getShares(); assertEquals(1, shares.size()); assertTrue(shares.contains(share)); } @Test - void testGetSharesBySymbolPortfolio() { - Portfolio portfolio = new Portfolio(); + void testGetSharesIsDefensiveCopy() { + portfolio.addShare(share); + List copy = portfolio.getShares(); + copy.clear(); + assertEquals(1, portfolio.getShares().size()); + } - Stock stock1 = new Stock("AAPL", "Apple", new BigDecimal("150")); + @Test + void testGetSharesBySymbol() { Stock stock2 = new Stock("GOOGL", "Google", new BigDecimal("200")); + Share share2 = new Share(stock2, new BigDecimal("5"), new BigDecimal("190")); - Share share1 = new Share(stock1, new BigDecimal("10"), new BigDecimal("140")); - Share share2 = new Share(stock2, new BigDecimal("10"), new BigDecimal("190")); - - portfolio.addShare(share1); + portfolio.addShare(share); portfolio.addShare(share2); - var result = portfolio.getShares("AAPL"); - + List result = portfolio.getShares("AAPL"); assertEquals(1, result.size()); - assertTrue(result.contains(share1)); + 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 testContainsPortfolio() { - Portfolio portfolio = new Portfolio(); + void testContainsReturnsFalseWhenAbsent() { + assertFalse(portfolio.contains(share)); + } - Stock stock = new Stock("AAPL", "Apple", new BigDecimal("150")); - Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); + @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); + } - assertTrue(portfolio.contains(share)); + // ---- 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(" ") + ); + } } diff --git a/src/test/java/PurchaseTest.java b/src/test/java/PurchaseTest.java index 2f87b39..d41e71a 100644 --- a/src/test/java/PurchaseTest.java +++ b/src/test/java/PurchaseTest.java @@ -1,4 +1,4 @@ -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.*; import java.math.BigDecimal; @@ -11,32 +11,93 @@ import Model.Stock; public class PurchaseTest { - Stock stock; - Share share; - Purchase purchase; + + private Stock stock; + private Share share; + private Purchase purchase; + private Player player; @BeforeEach void setUp() { - this.stock = new Stock("symbol", "company", new BigDecimal(100)); - this.share = new Share(this.stock, new BigDecimal(20), new BigDecimal(50)); - this.purchase = new Purchase(this.share, 18); + 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 commit_validPurchase_updatesPlayer() { + void testCommitDeductsMoneyFromPlayer() { + BigDecimal before = player.getMoney(); + purchase.commit(player); + assertTrue(player.getMoney().compareTo(before) < 0); + } - // Arrange - Player player = new Player("Jane", new BigDecimal(500000)); - BigDecimal startingMoney = player.getMoney(); + @Test + void testCommitAddsShareToPortfolio() { + purchase.commit(player); + assertTrue(player.getPortfolio().contains(share)); + } + + @Test + void testCommitRecordedInArchive() { + purchase.commit(player); + assertTrue(player.getTransactionArchive().getAllTransactions().contains(purchase)); + } - // Act - this.purchase.commit(player); + @Test + void testCommitSetsCommittedFlag() { + purchase.commit(player); + assertTrue(purchase.isCommitted()); + } - // Assert - assertEquals(1, startingMoney.subtract(player.getMoney()).signum()); + @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) + ); + } } diff --git a/src/test/java/SaleTest.java b/src/test/java/SaleTest.java index a17d1e2..622f262 100644 --- a/src/test/java/SaleTest.java +++ b/src/test/java/SaleTest.java @@ -6,57 +6,99 @@ import Model.Share; import Model.Stock; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import java.math.BigDecimal; public class SaleTest { - Stock stock; - Share share; - Sale sale; + + private Stock stock; + private Share share; + private Sale sale; + private Player player; @BeforeEach - void setUp(){ - this.stock = new Stock("symbol", "company", new BigDecimal(100)); - this.share = new Share(this.stock, new BigDecimal(20), new BigDecimal(80)); - this.sale = new Sale(this.share, 18); - } + 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 commit_validSale_updatesPlayerState() { - - // Arrange - Player player = new Player("Jane", new BigDecimal(500)); - player.getPortfolio().addShare(this.share); + 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)); + } - // Act - this.sale.commit(player); + @Test + void testCommitRecordedInArchive() { + sale.commit(player); + assertTrue(player.getTransactionArchive().getAllTransactions().contains(sale)); + } - // Assert - assertTrue(player.getTransactionArchive().getTransactions(this.sale.getWeek()).contains(this.sale)); - assertFalse(player.getPortfolio().contains(this.share)); - assertTrue(this.sale.isCommitted()); + @Test + void testCommitSetsCommittedFlag() { + sale.commit(player); + assertTrue(sale.isCommitted()); } @Test - void commit_alreadyCommitted_noAction() { + void testGetShareReturnsCorrectShare() { + assertEquals(share, sale.getShare()); + } - // Arrange - Player player = new Player("Jane", new BigDecimal(500)); - player.getPortfolio().addShare(this.share); - this.sale.commit(player); - BigDecimal moneyAfterFirstCommit = player.getMoney(); - - // Act - this.sale.commit(player); + @Test + void testGetWeekReturnsCorrectWeek() { + assertEquals(1, sale.getWeek()); + } - // Assert - assertEquals(moneyAfterFirstCommit, player.getMoney()); - assertEquals(1, player.getTransactionArchive().getTransactions(this.sale.getWeek()).size()); - assertTrue(this.sale.isCommitted()); - } + // ---- 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) + ); + } } diff --git a/src/test/java/ShareTest.java b/src/test/java/ShareTest.java index 3d840bb..fbc018d 100644 --- a/src/test/java/ShareTest.java +++ b/src/test/java/ShareTest.java @@ -1,5 +1,6 @@ import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import Model.Share; @@ -7,69 +8,89 @@ import java.math.BigDecimal; - public class ShareTest { - @Test - void testShareConstructor() { + private Stock stock; + + @BeforeEach + void setUp() { + stock = new Stock("AAPL", "Apple", new BigDecimal("150")); + } - Stock stock = new Stock("AAPL", "Apple", new BigDecimal("150")); - BigDecimal quantity = new BigDecimal("10"); - BigDecimal purchasePrice = new BigDecimal("140"); + // ---- Positive tests ---- - Share share = new Share(stock, quantity, purchasePrice); + @Test + void testShareConstructorNotNull() { + Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); assertNotNull(share); } @Test void testGetStock() { - - Stock stock = new Stock("AAPL", "Apple", new BigDecimal("150")); Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); - - Stock result = share.getStock(); - - assertEquals(stock, result); + assertEquals(stock, share.getStock()); } @Test void testGetQuantity() { - - Stock stock = new Stock("AAPL", "Apple", new BigDecimal("150")); Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); - - BigDecimal result = share.getQuantity(); - - assertEquals(new BigDecimal("10"), result); + assertEquals(new BigDecimal("10"), share.getQuantity()); } @Test void testGetPurchasePrice() { - - Stock stock = new Stock("AAPL", "Apple", new BigDecimal("150")); Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("140")); - - BigDecimal result = share.getPurchasePrice(); - - assertEquals(new BigDecimal("140"), result); + assertEquals(new BigDecimal("140"), share.getPurchasePrice()); } @Test - void testNullStock() { + 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()); + } - Share share = new Share(null, new BigDecimal("10"), new BigDecimal("100")); + // ---- Negative tests ---- - assertNull(share.getStock()); + @Test + void testNullStockThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Share(null, new BigDecimal("10"), new BigDecimal("100")) + ); } @Test - void testNegativePrice() { + void testNullQuantityThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Share(stock, null, new BigDecimal("100")) + ); + } - Stock stock = new Stock("AAPL", "Apple", new BigDecimal("150")); - Share share = new Share(stock, new BigDecimal("10"), new BigDecimal("-50")); + @Test + void testZeroQuantityThrows() { + assertThrows(IllegalArgumentException.class, () -> + new Share(stock, BigDecimal.ZERO, new BigDecimal("100")) + ); + } - assertEquals(new BigDecimal("-50"), share.getPurchasePrice()); + @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")) + ); + } } diff --git a/src/test/java/StockTest.java b/src/test/java/StockTest.java index fcc0d09..6a6a477 100644 --- a/src/test/java/StockTest.java +++ b/src/test/java/StockTest.java @@ -1,3 +1,4 @@ +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import Model.Stock; @@ -5,35 +6,159 @@ import static org.junit.jupiter.api.Assertions.*; import java.math.BigDecimal; - +import java.util.List; public class StockTest { - @Test - public void testGetSalesPrice() { - Stock stock = new Stock("AAPL", "APPLE", new BigDecimal("1000")); + 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 - public void testAddNewSalesPrice() { - Stock stock = new Stock("AAPL", "APPLE", new BigDecimal("1000")); + + @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 - public void testGetSalesPriceNone() { - Stock stock = new Stock("AAPL", "APPLE", new BigDecimal("0")); + @Test + void testAddNewSalesPriceZero() { + stock.addNewSalesPrice(new BigDecimal("0")); assertEquals(new BigDecimal("0"), stock.getSalesPrice()); } - @Test - public void testAddNewSalesPriceNone() { - Stock stock = new Stock("AAPL", "APPLE", new BigDecimal("1000")); - 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")) + ); + } } diff --git a/src/test/java/TransactionArchiveTest.java b/src/test/java/TransactionArchiveTest.java new file mode 100644 index 0000000..14ff009 --- /dev/null +++ b/src/test/java/TransactionArchiveTest.java @@ -0,0 +1,148 @@ +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; + +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) + ); + } +} diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst index f40803d..88de729 100644 --- a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -2,6 +2,7 @@ C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Control C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\Exchange.java C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\ExchangeObserver.java C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\Player.java +C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\PlayerStatus.java C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\Portfolio.java C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\Purchase.java C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\PurchaseCalculator.java @@ -12,6 +13,7 @@ C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\S C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\Transaction.java C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\TransactionArchive.java C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\TransactionCalculator.java +C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\TransactionFactory.java C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\View\GameSetupScene.java C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\View\Launcher.java C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\View\MainGameScene.java diff --git a/target/test-classes/ExchangeTest.class b/target/test-classes/ExchangeTest.class index 69ecbef..238ca9d 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 9be31bc..679e0c4 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 0c78c04..abf202d 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 1a94286..0008700 100644 Binary files a/target/test-classes/StockTest.class and b/target/test-classes/StockTest.class differ