From 77beda412f523cb29be953ae44f5f2257efa3d5f Mon Sep 17 00:00:00 2001 From: dhananjay-k-s Date: Thu, 5 Feb 2026 21:06:24 +0530 Subject: [PATCH 1/4] custom exception handling merging custom exception handling --- .../org/hsbc/exception/ErrorResponse.java | 62 ++++++++++++++ .../exception/GlobalExceptionHandler.java | 64 ++++++++++++++ .../InsufficientBalanceException.java | 7 ++ .../exception/ResourceNotFoundException.java | 7 ++ .../java/org/hsbc/service/PmsServiceimp.java | 83 +++++++++---------- .../org/hsbc/service/WalletServiceImpl.java | 15 ++-- 6 files changed, 183 insertions(+), 55 deletions(-) create mode 100644 src/main/java/org/hsbc/exception/ErrorResponse.java create mode 100644 src/main/java/org/hsbc/exception/GlobalExceptionHandler.java create mode 100644 src/main/java/org/hsbc/exception/InsufficientBalanceException.java create mode 100644 src/main/java/org/hsbc/exception/ResourceNotFoundException.java diff --git a/src/main/java/org/hsbc/exception/ErrorResponse.java b/src/main/java/org/hsbc/exception/ErrorResponse.java new file mode 100644 index 0000000..e84983f --- /dev/null +++ b/src/main/java/org/hsbc/exception/ErrorResponse.java @@ -0,0 +1,62 @@ +package org.hsbc.exception; + +import java.time.LocalDateTime; + +public class ErrorResponse { + private LocalDateTime timestamp; + private int status; + private String error; + private String message; + private String path; + + public ErrorResponse() { + } + + public ErrorResponse(int status, String error, String message, String path) { + this.timestamp = LocalDateTime.now(); + this.status = status; + this.error = error; + this.message = message; + this.path = path; + } + + public LocalDateTime getTimestamp() { + return timestamp; + } + + public void setTimestamp(LocalDateTime timestamp) { + this.timestamp = timestamp; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } +} diff --git a/src/main/java/org/hsbc/exception/GlobalExceptionHandler.java b/src/main/java/org/hsbc/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..e29fdd8 --- /dev/null +++ b/src/main/java/org/hsbc/exception/GlobalExceptionHandler.java @@ -0,0 +1,64 @@ +package org.hsbc.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.WebRequest; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(ResourceNotFoundException.class) + public ResponseEntity handleResourceNotFoundException(ResourceNotFoundException ex, + WebRequest request) { + ErrorResponse errorResponse = new ErrorResponse( + HttpStatus.NOT_FOUND.value(), + HttpStatus.NOT_FOUND.getReasonPhrase(), + ex.getMessage(), + request.getDescription(false).replace("uri=", "")); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(InsufficientBalanceException.class) + public ResponseEntity handleInsufficientBalanceException(InsufficientBalanceException ex, + WebRequest request) { + ErrorResponse errorResponse = new ErrorResponse( + HttpStatus.BAD_REQUEST.value(), + HttpStatus.BAD_REQUEST.getReasonPhrase(), + ex.getMessage(), + request.getDescription(false).replace("uri=", "")); + return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(InvalidPmsIdException.class) + public ResponseEntity handleInvalidPmsIdException(InvalidPmsIdException ex, WebRequest request) { + ErrorResponse errorResponse = new ErrorResponse( + HttpStatus.NOT_FOUND.value(), + HttpStatus.NOT_FOUND.getReasonPhrase(), + ex.getMessage(), + request.getDescription(false).replace("uri=", "")); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(InvalidTransactionIdException.class) + public ResponseEntity handleInvalidTransactionIdException(InvalidTransactionIdException ex, + WebRequest request) { + ErrorResponse errorResponse = new ErrorResponse( + HttpStatus.NOT_FOUND.value(), + HttpStatus.NOT_FOUND.getReasonPhrase(), + ex.getMessage(), + request.getDescription(false).replace("uri=", "")); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleGlobalException(Exception ex, WebRequest request) { + ErrorResponse errorResponse = new ErrorResponse( + HttpStatus.INTERNAL_SERVER_ERROR.value(), + HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), + ex.getMessage(), + request.getDescription(false).replace("uri=", "")); + return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); + } +} diff --git a/src/main/java/org/hsbc/exception/InsufficientBalanceException.java b/src/main/java/org/hsbc/exception/InsufficientBalanceException.java new file mode 100644 index 0000000..07d8db8 --- /dev/null +++ b/src/main/java/org/hsbc/exception/InsufficientBalanceException.java @@ -0,0 +1,7 @@ +package org.hsbc.exception; + +public class InsufficientBalanceException extends RuntimeException { + public InsufficientBalanceException(String message) { + super(message); + } +} diff --git a/src/main/java/org/hsbc/exception/ResourceNotFoundException.java b/src/main/java/org/hsbc/exception/ResourceNotFoundException.java new file mode 100644 index 0000000..3d35dca --- /dev/null +++ b/src/main/java/org/hsbc/exception/ResourceNotFoundException.java @@ -0,0 +1,7 @@ +package org.hsbc.exception; + +public class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/org/hsbc/service/PmsServiceimp.java b/src/main/java/org/hsbc/service/PmsServiceimp.java index 3128496..63c0a40 100644 --- a/src/main/java/org/hsbc/service/PmsServiceimp.java +++ b/src/main/java/org/hsbc/service/PmsServiceimp.java @@ -1,6 +1,5 @@ package org.hsbc.service; - //Import statemnts import org.hsbc.entity.PmsEntity; import org.hsbc.exception.InvalidPmsIdException; @@ -8,40 +7,37 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Service; -import org.springframework.web.server.ResponseStatusException; - import java.time.LocalDate; +import org.springframework.stereotype.Service; import java.util.List; import java.util.Optional; @Service public class PmsServiceimp implements PmsService { - private static final Logger log = - LoggerFactory.getLogger(PmsServiceimp.class); - @Autowired - private PmsRepository repository; - - @Autowired - private WalletService walletService; - - // 1️⃣ Add Asset - @Override - public PmsEntity addAsset(PmsEntity asset) { - // Calculate total cost - double totalCost = asset.getBuyPrice() * asset.getQuantity(); - - // Check and deduct from wallet (this will throw exception if insufficient balance) - walletService.deductMoney(totalCost); - - // Set purchase date and buying value - asset.setPurchaseDate(LocalDate.now()); - asset.setBuyingValue(totalCost); - - return repository.save(asset); - } + private static final Logger log = LoggerFactory.getLogger(PmsServiceimp.class); + @Autowired + private PmsRepository repository; + + @Autowired + private WalletService walletService; + + // 1️⃣ Add Asset + @Override + public PmsEntity addAsset(PmsEntity asset) { + // Calculate total cost + double totalCost = asset.getBuyPrice() * asset.getQuantity(); + + // Check and deduct from wallet (this will throw exception if insufficient + // balance) + walletService.deductMoney(totalCost); + + // Set purchase date and buying value + asset.setPurchaseDate(LocalDate.now()); + asset.setBuyingValue(totalCost); + + return repository.save(asset); + } // 2️⃣ Remove Asset @Override @@ -80,7 +76,8 @@ public double calculatePLPercentage(Long id) throws InvalidPmsIdException { double buyingValue = asset.getBuyPrice() * asset.getQuantity(); double pl = calculatePL(id); - if (buyingValue == 0) return 0; + if (buyingValue == 0) + return 0; return (pl / buyingValue) * 100; } @@ -93,6 +90,7 @@ public double getTotalPortfolioValue() { .mapToDouble(a -> a.getCurrentPrice() * a.getQuantity()) .sum(); } + public PmsServiceimp(PmsRepository repository) { this.repository = repository; } @@ -101,6 +99,7 @@ public PmsServiceimp(PmsRepository repository) { public List getAllAssets() { return repository.findAll(); } + @Override public PmsEntity getAssetById(Long id) throws InvalidPmsIdException { Optional optAsset = repository.findById(id); @@ -120,22 +119,16 @@ public PmsEntity updateCurrentPrice(String symbol, double newPrice) { return repository.save(asset); } } - throw new ResponseStatusException( - HttpStatus.NOT_FOUND, - "Asset not found with symbol " + symbol - ); + throw new org.hsbc.exception.ResourceNotFoundException("Asset not found with symbol " + symbol); } -// -// public PmsEntity findAllPms(long id) throws InvalidException { -// Optional optProduct = repository.findById(id); -// PmsEntity pmsEntity = optProduct.orElseThrow( -// ()->new InvalidException("Id is not valid: " + id) -// ); -// -// return pmsEntity; -// } - - - + // + // public PmsEntity findAllPms(long id) throws InvalidException { + // Optional optProduct = repository.findById(id); + // PmsEntity pmsEntity = optProduct.orElseThrow( + // ()->new InvalidException("Id is not valid: " + id) + // ); + // + // return pmsEntity; + // } } \ No newline at end of file diff --git a/src/main/java/org/hsbc/service/WalletServiceImpl.java b/src/main/java/org/hsbc/service/WalletServiceImpl.java index f947f7d..6053d24 100644 --- a/src/main/java/org/hsbc/service/WalletServiceImpl.java +++ b/src/main/java/org/hsbc/service/WalletServiceImpl.java @@ -4,15 +4,12 @@ import org.hsbc.repo.WalletRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; -import org.springframework.web.server.ResponseStatusException; @Service -public class WalletServiceImpl implements WalletService{ +public class WalletServiceImpl implements WalletService { - private static final Logger log = - LoggerFactory.getLogger(WalletServiceImpl.class); + private static final Logger log = LoggerFactory.getLogger(WalletServiceImpl.class); private final WalletRepository repository; @@ -22,8 +19,7 @@ public WalletServiceImpl(WalletRepository repository) { private WalletEntity getWallet() { return repository.findById(1L) - .orElseThrow(() -> new ResponseStatusException( - HttpStatus.NOT_FOUND, "Wallet not found")); + .orElseThrow(() -> new org.hsbc.exception.ResourceNotFoundException("Wallet not found")); } @Override @@ -44,15 +40,14 @@ public double deductMoney(double amount) { WalletEntity wallet = getWallet(); if (wallet.getBalance() < amount) { - throw new ResponseStatusException( - HttpStatus.BAD_REQUEST, "Insufficient balance"); + throw new org.hsbc.exception.InsufficientBalanceException("Insufficient balance"); } wallet.setBalance(wallet.getBalance() - amount); repository.save(wallet); return wallet.getBalance(); } - + @Override public WalletEntity getWalletSummary() { return getWallet(); From e0453c693a19223280805965fe08ab792733b8b6 Mon Sep 17 00:00:00 2001 From: dhananjay-k-s Date: Thu, 5 Feb 2026 21:30:20 +0530 Subject: [PATCH 2/4] Test cases increased This is to increase the test coverage --- .../hsbc/controller/PmsControllerTest.java | 59 ++++++- .../controller/PortfolioControllerTest.java | 160 ++++++++++++++++++ .../hsbc/controller/WalletControllerTest.java | 27 ++- .../exception/GlobalExceptionHandlerTest.java | 96 +++++++++++ .../org/hsbc/service/PmsServiceimpTest.java | 68 ++++++++ .../hsbc/service/WalletServiceimplTest.java | 5 +- 6 files changed, 401 insertions(+), 14 deletions(-) create mode 100644 src/test/java/org/hsbc/controller/PortfolioControllerTest.java create mode 100644 src/test/java/org/hsbc/exception/GlobalExceptionHandlerTest.java diff --git a/src/test/java/org/hsbc/controller/PmsControllerTest.java b/src/test/java/org/hsbc/controller/PmsControllerTest.java index 3b6c522..5656b6f 100644 --- a/src/test/java/org/hsbc/controller/PmsControllerTest.java +++ b/src/test/java/org/hsbc/controller/PmsControllerTest.java @@ -38,9 +38,9 @@ void testAddAsset() throws Exception { when(service.addAsset(any(PmsEntity.class))).thenReturn(asset); - mockMvc.perform(post("/pms/add") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(asset))) + mockMvc.perform(post("/api/pms/add") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(asset))) .andExpect(status().isOk()) .andExpect(jsonPath("$.companyName").value("Apple")) .andExpect(jsonPath("$.quantity").value(10)); @@ -51,7 +51,7 @@ void testAddAsset() throws Exception { void testRemoveAsset() throws Exception { doNothing().when(service).removeAsset(1L); - mockMvc.perform(delete("/pms/remove/1")) + mockMvc.perform(delete("/api/pms/remove/1")) .andExpect(status().isOk()) .andExpect(content().string("Asset removed successfully")); } @@ -65,8 +65,8 @@ void testUpdateQuantity() throws Exception { when(service.updateQuantity(1L, 20)).thenReturn(updated); - mockMvc.perform(put("/pms/update-quantity/1") - .param("quantity", "20")) + mockMvc.perform(put("/api/pms/update-quantity/1") + .param("quantity", "20")) .andExpect(status().isOk()) .andExpect(jsonPath("$.quantity").value(20)); } @@ -76,17 +76,25 @@ void testUpdateQuantity() throws Exception { void testGetPL() throws Exception { when(service.calculatePL(1L)).thenReturn(200.0); - mockMvc.perform(get("/pms/pl/1")) + mockMvc.perform(get("/api/pms/pl/1")) .andExpect(status().isOk()) .andExpect(content().string("200.0")); } + @Test + void testGetPL_NotFound() throws Exception { + when(service.calculatePL(99L)).thenThrow(new org.hsbc.exception.InvalidPmsIdException("Not Found")); + + mockMvc.perform(get("/api/pms/pl/99")) + .andExpect(status().isNotFound()); + } + // 5️⃣ GET /pms/pl-percentage/{id} @Test void testGetPLPercentage() throws Exception { when(service.calculatePLPercentage(1L)).thenReturn(20.0); - mockMvc.perform(get("/pms/pl-percentage/1")) + mockMvc.perform(get("/api/pms/pl-percentage/1")) .andExpect(status().isOk()) .andExpect(content().string("20.0")); } @@ -96,8 +104,41 @@ void testGetPLPercentage() throws Exception { void testGetTotalValue() throws Exception { when(service.getTotalPortfolioValue()).thenReturn(1200.0); - mockMvc.perform(get("/pms/total-value")) + mockMvc.perform(get("/api/pms/total-value")) .andExpect(status().isOk()) .andExpect(content().string("1200.0")); } + + @Test + void testGetAssetById() throws Exception { + PmsEntity asset = new PmsEntity(); + asset.setId(1L); + when(service.getAssetById(1L)).thenReturn(asset); + + mockMvc.perform(get("/api/pms/1")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(1)); + } + + @Test + void testGetAssetById_NotFound() throws Exception { + when(service.getAssetById(99L)).thenThrow(new org.hsbc.exception.InvalidPmsIdException("Not Found")); + + mockMvc.perform(get("/api/pms/99")) + .andExpect(status().isNotFound()); + } + + @Test + void testUpdatePrice() throws Exception { + PmsEntity asset = new PmsEntity(); + asset.setSymbol("AAPL"); + asset.setCurrentPrice(150.0); + + when(service.updateCurrentPrice("AAPL", 150.0)).thenReturn(asset); + + mockMvc.perform(put("/api/pms/update-price/AAPL") + .param("price", "150.0")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.currentPrice").value(150.0)); + } } diff --git a/src/test/java/org/hsbc/controller/PortfolioControllerTest.java b/src/test/java/org/hsbc/controller/PortfolioControllerTest.java new file mode 100644 index 0000000..2651e07 --- /dev/null +++ b/src/test/java/org/hsbc/controller/PortfolioControllerTest.java @@ -0,0 +1,160 @@ +package org.hsbc.controller; + +import org.hsbc.entity.PmsEntity; +import org.hsbc.service.PmsService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.client.RestTemplate; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.Matchers.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@WebMvcTest(PortfolioController.class) +class PortfolioControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private PmsService pmsService; + + @Autowired + private PortfolioController portfolioController; + + private RestTemplate restTemplateMock; + + @BeforeEach + void setUp() { + restTemplateMock = mock(RestTemplate.class); + ReflectionTestUtils.setField(portfolioController, "restTemplate", restTemplateMock); + } + + @Test + void testGetPortfolioSummary() throws Exception { + PmsEntity asset = new PmsEntity(); + asset.setBuyPrice(100.0); + asset.setQuantity(10); // Invested: 1000 + asset.setCurrentPrice(120.0); // Current: 1200 + asset.setBuyingValue(1000.0); + + when(pmsService.getAllAssets()).thenReturn(Collections.singletonList(asset)); + + mockMvc.perform(get("/api/portfolio/summary")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.portfolioValue").value(1200.0)) + .andExpect(jsonPath("$.totalInvested").value(1000.0)) + .andExpect(jsonPath("$.totalGain").value(200.0)) + .andExpect(jsonPath("$.gainPercentage").value(20.0)); + } + + @Test + void testGetPortfolioPerformance() throws Exception { + PmsEntity asset = new PmsEntity(); + asset.setSymbol("AAPL"); + asset.setQuantity(10); + asset.setAssetType("Stocks"); + + when(pmsService.getAllAssets()).thenReturn(Collections.singletonList(asset)); + + String mockApiResponse = "{\"data\": [{\"time\": \"2023-01-01\", \"close\": 150.0}]}"; + when(restTemplateMock.getForObject(anyString(), eq(String.class))).thenReturn(mockApiResponse); + + mockMvc.perform(get("/api/portfolio/performance")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(1))) + .andExpect(jsonPath("$[0].date").value("2023-01-01")) + .andExpect(jsonPath("$[0].value").value(1500)); // 150 * 10 + } + + @Test + void testGetPortfolioPerformance_EmptyAssets() throws Exception { + when(pmsService.getAllAssets()).thenReturn(Collections.emptyList()); + + mockMvc.perform(get("/api/portfolio/performance")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(0))); + } + + @Test + void testGetAssetAllocation() throws Exception { + PmsEntity stock = new PmsEntity(); + stock.setAssetType("Stocks"); + stock.setCurrentPrice(100.0); + stock.setQuantity(10); // 1000 + + PmsEntity crypto = new PmsEntity(); + crypto.setAssetType("Crypto"); + crypto.setCurrentPrice(50.0); + crypto.setQuantity(20); // 1000 + + when(pmsService.getAllAssets()).thenReturn(Arrays.asList(stock, crypto)); + + mockMvc.perform(get("/api/portfolio/allocation")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[*].assetType", containsInAnyOrder("Stocks", "Crypto"))) + .andExpect(jsonPath("$[*].value", containsInAnyOrder(1000.0, 1000.0))); + } + + @Test + void testGetInvestmentBreakdown() throws Exception { + PmsEntity stock = new PmsEntity(); + stock.setAssetType("Stock"); // Should normalize to Stocks + stock.setBuyingValue(1000.0); + + when(pmsService.getAllAssets()).thenReturn(Collections.singletonList(stock)); + + mockMvc.perform(get("/api/portfolio/breakdown")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(1))) + .andExpect(jsonPath("$[0].type").value("Stocks")) + .andExpect(jsonPath("$[0].value").value(1000.0)); + } + + @Test + void testGetInvestmentBreakdown_Empty() throws Exception { + when(pmsService.getAllAssets()).thenReturn(Collections.emptyList()); + + mockMvc.perform(get("/api/portfolio/breakdown")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(4))); // Expects all 4 types with 0 + } + + @Test + void testGetPerformers() throws Exception { + PmsEntity winner = new PmsEntity(); + winner.setSymbol("WIN"); + winner.setBuyPrice(100.0); + winner.setBuyingValue(1000.00); // Set buying value explicit + winner.setCurrentPrice(200.0); + winner.setQuantity(10); + + PmsEntity loser = new PmsEntity(); + loser.setSymbol("LOSE"); + loser.setBuyPrice(100.0); + loser.setBuyingValue(1000.00); // Set buying value explicit + loser.setCurrentPrice(50.0); + loser.setQuantity(10); + + when(pmsService.getAllAssets()).thenReturn(Arrays.asList(winner, loser)); + + mockMvc.perform(get("/api/portfolio/performers")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.topPerformers[0].symbol").value("WIN")) + .andExpect(jsonPath("$.lowestPerformers[0].symbol").value("LOSE")); + } +} diff --git a/src/test/java/org/hsbc/controller/WalletControllerTest.java b/src/test/java/org/hsbc/controller/WalletControllerTest.java index 69da6bb..65929d8 100644 --- a/src/test/java/org/hsbc/controller/WalletControllerTest.java +++ b/src/test/java/org/hsbc/controller/WalletControllerTest.java @@ -1,5 +1,6 @@ package org.hsbc.controller; +import org.hsbc.entity.WalletEntity; import org.hsbc.service.WalletService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -14,6 +15,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @ExtendWith(MockitoExtension.class) @@ -51,7 +53,7 @@ void testAddMoney() throws Exception { // Act & Assert // Note: controller expects @RequestParam, so we use .param("amount", ...) mockMvc.perform(post("/wallet/add") - .param("amount", "1000.0")) + .param("amount", "1000.0")) .andExpect(status().isOk()) .andExpect(content().string("6000.0")); } @@ -64,8 +66,29 @@ void testDeductMoney() throws Exception { // Act & Assert mockMvc.perform(post("/wallet/deduct") - .param("amount", "500.0")) + .param("amount", "500.0")) .andExpect(status().isOk()) .andExpect(content().string("4500.0")); } + + @Test + void testDeductMoney_InsufficientBalance() throws Exception { + when(service.deductMoney(10000.0)) + .thenThrow(new org.hsbc.exception.InsufficientBalanceException("Insufficient balance")); + + mockMvc.perform(post("/wallet/deduct") + .param("amount", "10000.0")) + .andExpect(status().isBadRequest()); + } + + @Test + void testGetWalletSummary() throws Exception { + WalletEntity wallet = new WalletEntity(); + wallet.setBalance(5000.0); + when(service.getWalletSummary()).thenReturn(wallet); + + mockMvc.perform(get("/wallet/summary")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.balance").value(5000.0)); + } } \ No newline at end of file diff --git a/src/test/java/org/hsbc/exception/GlobalExceptionHandlerTest.java b/src/test/java/org/hsbc/exception/GlobalExceptionHandlerTest.java new file mode 100644 index 0000000..837bad9 --- /dev/null +++ b/src/test/java/org/hsbc/exception/GlobalExceptionHandlerTest.java @@ -0,0 +1,96 @@ +package org.hsbc.exception; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(MockitoExtension.class) +class GlobalExceptionHandlerTest { + + private MockMvc mockMvc; + + @BeforeEach + void setUp() { + mockMvc = MockMvcBuilders.standaloneSetup(new TestController()) + .setControllerAdvice(new GlobalExceptionHandler()) + .build(); + } + + @Test + void testHandleResourceNotFoundException() throws Exception { + mockMvc.perform(get("/test/resource-not-found") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.message").value("Resource not found")); + } + + @Test + void testHandleInsufficientBalanceException() throws Exception { + mockMvc.perform(get("/test/insufficient-balance") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("Insufficient balance")); + } + + @Test + void testHandleInvalidPmsIdException() throws Exception { + mockMvc.perform(get("/test/invalid-pms-id") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.message").value("Invalid PMS ID")); + } + + @Test + void testHandleInvalidTransactionIdException() throws Exception { + mockMvc.perform(get("/test/invalid-transaction-id") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.message").value("Invalid Transaction ID")); + } + + @Test + void testHandleGlobalException() throws Exception { + mockMvc.perform(get("/test/global-exception") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.message").value("Internal error")); + } + + @RestController + static class TestController { + @GetMapping("/test/resource-not-found") + public void throwResourceNotFound() { + throw new ResourceNotFoundException("Resource not found"); + } + + @GetMapping("/test/insufficient-balance") + public void throwInsufficientBalance() { + throw new InsufficientBalanceException("Insufficient balance"); + } + + @GetMapping("/test/invalid-pms-id") + public void throwInvalidPmsId() throws InvalidPmsIdException { + throw new InvalidPmsIdException("Invalid PMS ID"); + } + + @GetMapping("/test/invalid-transaction-id") + public void throwInvalidTransactionId() throws InvalidTransactionIdException { + throw new InvalidTransactionIdException("Invalid Transaction ID"); + } + + @GetMapping("/test/global-exception") + public void throwGlobalException() throws Exception { + throw new Exception("Internal error"); + } + } +} diff --git a/src/test/java/org/hsbc/service/PmsServiceimpTest.java b/src/test/java/org/hsbc/service/PmsServiceimpTest.java index 06c1c2c..e742f98 100644 --- a/src/test/java/org/hsbc/service/PmsServiceimpTest.java +++ b/src/test/java/org/hsbc/service/PmsServiceimpTest.java @@ -28,6 +28,9 @@ public class PmsServiceimpTest { private PmsEntity asset1; + @Mock + private org.hsbc.service.WalletService walletService; + @BeforeEach void setUp() { // Setup dummy data @@ -41,6 +44,23 @@ void setUp() { // --- Success Tests --- + @Test + void testAddAsset_Success() { + PmsEntity newAsset = new PmsEntity(); + newAsset.setSymbol("GOOGL"); + newAsset.setBuyPrice(100.0); + newAsset.setQuantity(10); + + when(walletService.deductMoney(1000.0)).thenReturn(4000.0); + when(repository.save(any(PmsEntity.class))).thenReturn(newAsset); + + PmsEntity result = service.addAsset(newAsset); + + assertNotNull(result); + verify(walletService, times(1)).deductMoney(1000.0); + assertNotNull(result.getPurchaseDate()); + } + @Test void testGetAssetById_Success() throws InvalidPmsIdException { when(repository.findById(1L)).thenReturn(Optional.of(asset1)); @@ -75,6 +95,23 @@ void testCalculatePL_Success() throws InvalidPmsIdException { assertEquals(200.0, pl); } + @Test + void testCalculatePLPercentage_Success() throws InvalidPmsIdException { + when(repository.findById(1L)).thenReturn(Optional.of(asset1)); + // PL is 200. Buying value 1500. % = 200/1500 * 100 = 13.333 + + double plPct = service.calculatePLPercentage(1L); + assertEquals(13.333, plPct, 0.01); + } + + @Test + void testCalculatePLPercentage_ZeroBuyingValue() throws InvalidPmsIdException { + asset1.setBuyPrice(0.0); + when(repository.findById(1L)).thenReturn(Optional.of(asset1)); + + assertEquals(0.0, service.calculatePLPercentage(1L)); + } + @Test void testRemoveAsset_Success() throws InvalidPmsIdException { when(repository.findById(1L)).thenReturn(Optional.of(asset1)); @@ -84,6 +121,37 @@ void testRemoveAsset_Success() throws InvalidPmsIdException { verify(repository, times(1)).deleteById(1L); } + @Test + void testGetTotalPortfolioValue() { + PmsEntity asset2 = new PmsEntity(); + asset2.setCurrentPrice(50.0); + asset2.setQuantity(20); // 1000 + // asset1 is 170 * 10 = 1700 + + when(repository.findAll()).thenReturn(Arrays.asList(asset1, asset2)); + + double total = service.getTotalPortfolioValue(); + assertEquals(2700.0, total); + } + + @Test + void testUpdateCurrentPrice_Success() { + when(repository.findAll()).thenReturn(Arrays.asList(asset1)); + when(repository.save(any(PmsEntity.class))).thenReturn(asset1); + + service.updateCurrentPrice("AAPL", 180.0); + + assertEquals(180.0, asset1.getCurrentPrice()); + } + + @Test + void testUpdateCurrentPrice_NotFound() { + when(repository.findAll()).thenReturn(Arrays.asList(asset1)); + + assertThrows(org.hsbc.exception.ResourceNotFoundException.class, + () -> service.updateCurrentPrice("XYZ", 100.0)); + } + // --- Exception Tests (Failure Scenarios) --- @Test diff --git a/src/test/java/org/hsbc/service/WalletServiceimplTest.java b/src/test/java/org/hsbc/service/WalletServiceimplTest.java index 8c600f9..aad5c4d 100644 --- a/src/test/java/org/hsbc/service/WalletServiceimplTest.java +++ b/src/test/java/org/hsbc/service/WalletServiceimplTest.java @@ -8,7 +8,6 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.web.server.ResponseStatusException; import java.util.Optional; @@ -50,7 +49,7 @@ void testGetBalance() { void testGetBalanceNotFound() { when(repository.findById(1L)).thenReturn(Optional.empty()); - assertThrows(ResponseStatusException.class, () -> service.getBalance()); + assertThrows(org.hsbc.exception.ResourceNotFoundException.class, () -> service.getBalance()); } // 2️⃣ addMoney() @@ -85,7 +84,7 @@ void testDeductMoneyInsufficientBalance() { when(repository.findById(1L)).thenReturn(Optional.of(wallet)); // Trying to deduct 6000 when balance is 5000 - assertThrows(ResponseStatusException.class, () -> service.deductMoney(6000.0)); + assertThrows(org.hsbc.exception.InsufficientBalanceException.class, () -> service.deductMoney(6000.0)); // Ensure save is NEVER called if exception is thrown verify(repository, times(0)).save(any(WalletEntity.class)); From 58596571f8cca54b9f9864e732649a4091b88867 Mon Sep 17 00:00:00 2001 From: dhananjay-k-s Date: Thu, 5 Feb 2026 21:50:20 +0530 Subject: [PATCH 3/4] test cases increased --- .../controller/StockDataControllerTest.java | 427 ++++++++++-------- .../java/org/hsbc/entity/PmsEntityTest.java | 83 ++++ .../hsbc/entity/TransactionEntityTest.java | 64 +++ .../org/hsbc/entity/WalletEntityTest.java | 40 ++ .../hsbc/exception/ExceptionClassesTest.java | 77 ++++ 5 files changed, 507 insertions(+), 184 deletions(-) create mode 100644 src/test/java/org/hsbc/entity/PmsEntityTest.java create mode 100644 src/test/java/org/hsbc/entity/TransactionEntityTest.java create mode 100644 src/test/java/org/hsbc/entity/WalletEntityTest.java create mode 100644 src/test/java/org/hsbc/exception/ExceptionClassesTest.java diff --git a/src/test/java/org/hsbc/controller/StockDataControllerTest.java b/src/test/java/org/hsbc/controller/StockDataControllerTest.java index 586f6dc..c772fbe 100644 --- a/src/test/java/org/hsbc/controller/StockDataControllerTest.java +++ b/src/test/java/org/hsbc/controller/StockDataControllerTest.java @@ -1,197 +1,256 @@ - package org.hsbc.controller; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; -import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; - -@ExtendWith(MockitoExtension.class) -public class StockDataControllerTest { - - private MockMvc mockMvc; - - @Mock - private RestTemplate restTemplate; - - @InjectMocks - private StockDataController stockDataController; - - private final String FLASK_URL = "http://localhost:5000"; - - @BeforeEach - void setUp() { - // 1. Inject the Flask URL value (simulating @Value) - ReflectionTestUtils.setField(stockDataController, "flaskApiUrl", FLASK_URL); - - // 2. Overwrite the hardcoded RestTemplate with our Mock using Reflection - // This is necessary because the controller does 'new RestTemplate()' in the constructor - ReflectionTestUtils.setField(stockDataController, "restTemplate", restTemplate); - - // 3. Build the standalone MockMvc context - mockMvc = MockMvcBuilders.standaloneSetup(stockDataController).build(); - } - - // ========================================== - // 1. GET Stock Tests - // ========================================== - - @Test - void getStock_Success() throws Exception { - String mockResponse = "{\"symbol\":\"AAPL\",\"price\":150.0}"; - when(restTemplate.getForEntity(eq(FLASK_URL + "/api/stocks/AAPL"), eq(String.class))) - .thenReturn(new ResponseEntity<>(mockResponse, HttpStatus.OK)); - - mockMvc.perform(get("/api/yfdata/stocks/AAPL")) - .andExpect(status().isOk()) - .andExpect(content().json(mockResponse)); - } - - @Test - void getStock_NotFound() throws Exception { - when(restTemplate.getForEntity(anyString(), eq(String.class))) - .thenThrow( HttpClientErrorException.NotFound.class); - - mockMvc.perform(get("/api/yfdata/stocks/UNKNOWN")) - .andExpect(status().isNotFound()) - .andExpect(jsonPath("$.error").value("Stock not found")); - } - - @Test - void getStock_ServerError() throws Exception { - when(restTemplate.getForEntity(anyString(), eq(String.class))) - .thenThrow(new RuntimeException("Flask down")); - - mockMvc.perform(get("/api/yfdata/stocks/AAPL")) - .andExpect(status().isInternalServerError()) - .andExpect(jsonPath("$.error").exists()); - } - - // ========================================== - // 2. GET Crypto Tests - // ========================================== - - @Test - void getCrypto_Success() throws Exception { - String mockResponse = "{\"symbol\":\"BTC\",\"price\":50000.0}"; - when(restTemplate.getForEntity(eq(FLASK_URL + "/api/crypto/BTC"), eq(String.class))) - .thenReturn(new ResponseEntity<>(mockResponse, HttpStatus.OK)); - - mockMvc.perform(get("/api/yfdata/crypto/BTC")) - .andExpect(status().isOk()) - .andExpect(content().json(mockResponse)); - } - - // ========================================== - // 3. GET Mutual Fund Tests - // ========================================== - - @Test - void getMutualFund_Success() throws Exception { - String mockResponse = "{\"symbol\":\"VFIAX\",\"nav\":400.0}"; - when(restTemplate.getForEntity(eq(FLASK_URL + "/api/mutual-funds/VFIAX"), eq(String.class))) - .thenReturn(new ResponseEntity<>(mockResponse, HttpStatus.OK)); - - mockMvc.perform(get("/api/yfdata/mutual-funds/VFIAX")) - .andExpect(status().isOk()) - .andExpect(content().json(mockResponse)); - } - - // ========================================== - // 4. GET Commodity Tests - // ========================================== - - @Test - void getCommodity_Success() throws Exception { - String mockResponse = "{\"symbol\":\"GC=F\",\"price\":1900.0}"; - when(restTemplate.getForEntity(eq(FLASK_URL + "/api/commodities/GC=F"), eq(String.class))) - .thenReturn(new ResponseEntity<>(mockResponse, HttpStatus.OK)); - - mockMvc.perform(get("/api/yfdata/commodities/GC=F")) - .andExpect(status().isOk()) - .andExpect(content().json(mockResponse)); - } - - // ========================================== - // 5. GET History Tests - // ========================================== - - @Test - void getHistory_DefaultPeriod() throws Exception { - String mockResponse = "{\"history\":[]}"; - // Verify default period is 1MO - when(restTemplate.getForEntity(eq(FLASK_URL + "/api/history/AAPL?period=1MO"), eq(String.class))) - .thenReturn(new ResponseEntity<>(mockResponse, HttpStatus.OK)); - - mockMvc.perform(get("/api/yfdata/history/AAPL")) - .andExpect(status().isOk()) - .andExpect(content().json(mockResponse)); - } - - @Test - void getHistory_CustomPeriod() throws Exception { - String mockResponse = "{\"history\":[]}"; - // Verify custom period 1Y - when(restTemplate.getForEntity(eq(FLASK_URL + "/api/history/AAPL?period=1Y"), eq(String.class))) - .thenReturn(new ResponseEntity<>(mockResponse, HttpStatus.OK)); - - mockMvc.perform(get("/api/yfdata/history/AAPL").param("period", "1Y")) - .andExpect(status().isOk()); - } - - // ========================================== - // 6. GET News Tests - // ========================================== - - @Test - void getNews_Success() throws Exception { - String mockResponse = "[{\"title\":\"News 1\"}]"; - when(restTemplate.getForEntity(eq(FLASK_URL + "/api/news/AAPL"), eq(String.class))) - .thenReturn(new ResponseEntity<>(mockResponse, HttpStatus.OK)); - - mockMvc.perform(get("/api/yfdata/news/AAPL")) - .andExpect(status().isOk()) - .andExpect(content().json(mockResponse)); - } - - // ========================================== - // 7. GET Search Tests - // ========================================== - - @Test - void searchAssets_Success() throws Exception { - String mockResponse = "[{\"symbol\":\"AAPL\"}]"; - when(restTemplate.getForEntity(eq(FLASK_URL + "/api/search?q=Apple"), eq(String.class))) - .thenReturn(new ResponseEntity<>(mockResponse, HttpStatus.OK)); - - mockMvc.perform(get("/api/yfdata/search").param("q", "Apple")) - .andExpect(status().isOk()) - .andExpect(content().json(mockResponse)); - } - - @Test - void searchAssets_MissingQuery() throws Exception { - // The controller checks if q is null or empty manually - mockMvc.perform(get("/api/yfdata/search").param("q", "")) - .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.error").value("Query parameter 'q' is required")); - } -} - -// =========================== \ No newline at end of file +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@WebMvcTest(StockDataController.class) +class StockDataControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private StockDataController controller; + + private RestTemplate restTemplate; + + @BeforeEach + void setUp() { + restTemplate = mock(RestTemplate.class); + ReflectionTestUtils.setField(controller, "restTemplate", restTemplate); + ReflectionTestUtils.setField(controller, "flaskApiUrl", "http://localhost:5000"); + } + + @Test + void testGetStock_Success() throws Exception { + String json = "{\"symbol\":\"AAPL\",\"price\":150.0}"; + when(restTemplate.getForEntity(anyString(), eq(String.class))) + .thenReturn(ResponseEntity.ok(json)); + + mockMvc.perform(get("/api/yfdata/stocks/AAPL")) + .andExpect(status().isOk()) + .andExpect(content().json(json)); + } + + @Test + void testGetStock_NotFound() throws Exception { + when(restTemplate.getForEntity(anyString(), eq(String.class))) + .thenThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND)); + + mockMvc.perform(get("/api/yfdata/stocks/UNKNOWN")) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.error").value("Stock not found")); + } + + @Test + void testGetCrypto_Success() throws Exception { + String json = "{\"symbol\":\"BTC\",\"price\":40000.0}"; + when(restTemplate.getForEntity(anyString(), eq(String.class))) + .thenReturn(ResponseEntity.ok(json)); + + mockMvc.perform(get("/api/yfdata/crypto/BTC")) + .andExpect(status().isOk()) + .andExpect(content().json(json)); + } + + @Test + void testGetCrypto_NotFound() throws Exception { + when(restTemplate.getForEntity(anyString(), eq(String.class))) + .thenThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND)); + + mockMvc.perform(get("/api/yfdata/crypto/UNKNOWN")) + .andExpect(status().isNotFound()); + } + + @Test + void testGetMutualFund_Success() throws Exception { + String json = "{\"symbol\":\"VFIAX\",\"nav\":400.0}"; + when(restTemplate.getForEntity(anyString(), eq(String.class))) + .thenReturn(ResponseEntity.ok(json)); + + mockMvc.perform(get("/api/yfdata/mutual-funds/VFIAX")) + .andExpect(status().isOk()) + .andExpect(content().json(json)); + } + + @Test + void testGetMutualFund_NotFound() throws Exception { + when(restTemplate.getForEntity(anyString(), eq(String.class))) + .thenThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND)); + + mockMvc.perform(get("/api/yfdata/mutual-funds/UNKNOWN")) + .andExpect(status().isNotFound()); + } + + @Test + void testGetCommodity_Success() throws Exception { + String json = "{\"symbol\":\"GC=F\",\"price\":1800.0}"; + when(restTemplate.getForEntity(anyString(), eq(String.class))) + .thenReturn(ResponseEntity.ok(json)); + + mockMvc.perform(get("/api/yfdata/commodities/GC=F")) + .andExpect(status().isOk()) + .andExpect(content().json(json)); + } + + @Test + void testGetCommodity_NotFound() throws Exception { + when(restTemplate.getForEntity(anyString(), eq(String.class))) + .thenThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND)); + + mockMvc.perform(get("/api/yfdata/commodities/UNKNOWN")) + .andExpect(status().isNotFound()); + } + + @Test + void testGetHistory_Success() throws Exception { + String json = "{\"date\":\"2023-01-01\",\"close\":150.0}"; + when(restTemplate.getForEntity(anyString(), eq(String.class))) + .thenReturn(ResponseEntity.ok(json)); + + mockMvc.perform(get("/api/yfdata/history/AAPL?period=1MO&interval=1d")) + .andExpect(status().isOk()) + .andExpect(content().json(json)); + } + + @Test + void testGetHistory_NotFound() throws Exception { + when(restTemplate.getForEntity(anyString(), eq(String.class))) + .thenThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND)); + + mockMvc.perform(get("/api/yfdata/history/UNKNOWN")) + .andExpect(status().isNotFound()); + } + + @Test + void testGetNews_Success() throws Exception { + String json = "[{\"title\":\"News 1\"}]"; + when(restTemplate.getForEntity(anyString(), eq(String.class))) + .thenReturn(ResponseEntity.ok(json)); + + mockMvc.perform(get("/api/yfdata/news/AAPL")) + .andExpect(status().isOk()) + .andExpect(content().json(json)); + } + + @Test + void testGetNews_NotFound() throws Exception { + when(restTemplate.getForEntity(anyString(), eq(String.class))) + .thenThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND)); + + mockMvc.perform(get("/api/yfdata/news/UNKNOWN")) + .andExpect(status().isNotFound()); + } + + @Test + void testSearchAssets_Success() throws Exception { + String json = "[{\"symbol\":\"AAPL\"}]"; + when(restTemplate.getForEntity(anyString(), eq(String.class))) + .thenReturn(ResponseEntity.ok(json)); + + mockMvc.perform(get("/api/yfdata/search?q=Apple")) + .andExpect(status().isOk()) + .andExpect(content().json(json)); + } + + @Test + void testSearchAssets_EmptyQuery() throws Exception { + mockMvc.perform(get("/api/yfdata/search?q=")) + .andExpect(status().isBadRequest()); + } + + @Test + void testGetPortfolioPerformers_Success() throws Exception { + String requestBody = "[{\"symbol\":\"AAPL\",\"buyPrice\":100}]"; + String responseBody = "{\"top\":[]}"; + + when(restTemplate.postForEntity(anyString(), any(), eq(String.class))) + .thenReturn(ResponseEntity.ok(responseBody)); + + mockMvc.perform(post("/api/yfdata/portfolio/performers") + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody)) + .andExpect(status().isOk()) + .andExpect(content().json(responseBody)); + } + + @Test + void testGetPortfolioPerformers_BadRequest() throws Exception { + when(restTemplate.postForEntity(anyString(), any(), eq(String.class))) + .thenThrow(new HttpClientErrorException(HttpStatus.BAD_REQUEST)); + + mockMvc.perform(post("/api/yfdata/portfolio/performers") + .contentType(MediaType.APPLICATION_JSON) + .content("{}")) + .andExpect(status().isBadRequest()); + } + + @Test + void testGetPortfolioRecommendations_Success() throws Exception { + String requestBody = "[{\"symbol\":\"AAPL\"}]"; + String responseBody = "{\"recommendations\":[]}"; + + when(restTemplate.postForEntity(anyString(), any(), eq(String.class))) + .thenReturn(ResponseEntity.ok(responseBody)); + + mockMvc.perform(post("/api/yfdata/portfolio/recommendations") + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody)) + .andExpect(status().isOk()) + .andExpect(content().json(responseBody)); + } + + @Test + void testGetStockAnalysis_Success() throws Exception { + String json = "{\"sentiment\":\"Bullish\"}"; + when(restTemplate.getForEntity(anyString(), eq(String.class))) + .thenReturn(ResponseEntity.ok(json)); + + mockMvc.perform(get("/api/yfdata/stock/AAPL/analysis?inPortfolio=true&buyPrice=150.0")) + .andExpect(status().isOk()) + .andExpect(content().json(json)); + } + + @Test + void testGetStockAnalysis_MissingBuyPrice() throws Exception { + mockMvc.perform(get("/api/yfdata/stock/AAPL/analysis?inPortfolio=true")) + .andExpect(status().isBadRequest()); + } + + @Test + void testHealthCheck_Success() throws Exception { + String json = "{\"status\":\"healthy\"}"; + when(restTemplate.getForEntity(anyString(), eq(String.class))) + .thenReturn(ResponseEntity.ok(json)); + + mockMvc.perform(get("/api/yfdata/health")) + .andExpect(status().isOk()) + .andExpect(content().json(json)); + } + + @Test + void testHealthCheck_Failure() throws Exception { + when(restTemplate.getForEntity(anyString(), eq(String.class))) + .thenThrow(new RuntimeException("Connection refused")); + + mockMvc.perform(get("/api/yfdata/health")) + .andExpect(status().isServiceUnavailable()); + } +} \ No newline at end of file diff --git a/src/test/java/org/hsbc/entity/PmsEntityTest.java b/src/test/java/org/hsbc/entity/PmsEntityTest.java new file mode 100644 index 0000000..e34e4a5 --- /dev/null +++ b/src/test/java/org/hsbc/entity/PmsEntityTest.java @@ -0,0 +1,83 @@ +package org.hsbc.entity; + +import org.junit.jupiter.api.Test; +import java.time.LocalDate; +import static org.junit.jupiter.api.Assertions.*; + +class PmsEntityTest { + + @Test + void testNoArgsConstructor() { + PmsEntity entity = new PmsEntity(); + assertNotNull(entity); + } + + @Test + void testAllArgsConstructor() { + LocalDate date = LocalDate.now(); + PmsEntity entity = new PmsEntity(1L, "Apple", "AAPL", 10, 100.0, 150.0, 1000.0, "USD", "NASDAQ", "Tech", + "Stock"); + entity.setPurchaseDate(date); + + assertEquals(1L, entity.getId()); + assertEquals("Apple", entity.getCompanyName()); + assertEquals("AAPL", entity.getSymbol()); + assertEquals(10, entity.getQuantity()); + assertEquals(100.0, entity.getBuyPrice()); + assertEquals(150.0, entity.getCurrentPrice()); + assertEquals(1000.0, entity.getBuyingValue()); + assertEquals("USD", entity.getCurrency()); + assertEquals("NASDAQ", entity.getExchange()); + assertEquals("Tech", entity.getIndustry()); + assertEquals("Stock", entity.getAssetType()); + assertEquals(date, entity.getPurchaseDate()); + } + + @Test + void testConstructorWithoutId() { + PmsEntity entity = new PmsEntity("Apple", "AAPL", 10, 100.0, 150.0, 1000.0, "USD", "NASDAQ", "Tech", "Stock"); + assertEquals("Apple", entity.getCompanyName()); + } + + @Test + void testSettersAndGetters() { + PmsEntity entity = new PmsEntity(); + LocalDate date = LocalDate.now(); + + entity.setId(1L); + entity.setCompanyName("Apple"); + entity.setSymbol("AAPL"); + entity.setQuantity(10); + entity.setBuyPrice(100.0); + entity.setCurrentPrice(150.0); + entity.setBuyingValue(1000.0); + entity.setCurrency("USD"); + entity.setExchange("NASDAQ"); + entity.setIndustry("Tech"); + entity.setAssetType("Stock"); + entity.setPurchaseDate(date); + + assertEquals(1L, entity.getId()); + assertEquals("Apple", entity.getCompanyName()); + assertEquals("AAPL", entity.getSymbol()); + assertEquals(10, entity.getQuantity()); + assertEquals(100.0, entity.getBuyPrice()); + assertEquals(150.0, entity.getCurrentPrice()); + assertEquals(1000.0, entity.getBuyingValue()); + assertEquals("USD", entity.getCurrency()); + assertEquals("NASDAQ", entity.getExchange()); + assertEquals("Tech", entity.getIndustry()); + assertEquals("Stock", entity.getAssetType()); + assertEquals(date, entity.getPurchaseDate()); + } + + @Test + void testToString() { + PmsEntity entity = new PmsEntity(); + entity.setId(1L); + entity.setSymbol("AAPL"); + String str = entity.toString(); + assertTrue(str.contains("id=1")); + assertTrue(str.contains("symbol='AAPL'")); + } +} diff --git a/src/test/java/org/hsbc/entity/TransactionEntityTest.java b/src/test/java/org/hsbc/entity/TransactionEntityTest.java new file mode 100644 index 0000000..b3f62d8 --- /dev/null +++ b/src/test/java/org/hsbc/entity/TransactionEntityTest.java @@ -0,0 +1,64 @@ +package org.hsbc.entity; + +import org.junit.jupiter.api.Test; +import java.time.LocalDateTime; +import static org.junit.jupiter.api.Assertions.*; + +class TransactionEntityTest { + + @Test + void testNoArgsConstructor() { + TransactionEntity entity = new TransactionEntity(); + assertNotNull(entity); + } + + @Test + void testAllArgsConstructor() { + LocalDateTime now = LocalDateTime.now(); + TransactionEntity entity = new TransactionEntity(1L, "AAPL", 10, 150.0, now, "BUY"); + + assertEquals(1L, entity.getTransactionId()); + assertEquals("AAPL", entity.getSymbol()); + assertEquals(10, entity.getQuantity()); + assertEquals(150.0, entity.getBuyPrice()); + assertEquals(now, entity.getTransactionDate()); + assertEquals("BUY", entity.getTransactionType()); + } + + @Test + void testConstructorWithoutId() { + LocalDateTime now = LocalDateTime.now(); + TransactionEntity entity = new TransactionEntity("AAPL", 10, 150.0, now, "BUY"); + assertEquals("AAPL", entity.getSymbol()); + } + + @Test + void testSettersAndGetters() { + TransactionEntity entity = new TransactionEntity(); + LocalDateTime now = LocalDateTime.now(); + + entity.setTransactionId(1L); + entity.setSymbol("AAPL"); + entity.setQuantity(10); + entity.setBuyPrice(150.0); + entity.setTransactionDate(now); + entity.setTransactionType("BUY"); + + assertEquals(1L, entity.getTransactionId()); + assertEquals("AAPL", entity.getSymbol()); + assertEquals(10, entity.getQuantity()); + assertEquals(150.0, entity.getBuyPrice()); + assertEquals(now, entity.getTransactionDate()); + assertEquals("BUY", entity.getTransactionType()); + } + + @Test + void testToString() { + TransactionEntity entity = new TransactionEntity(); + entity.setTransactionId(1L); + entity.setSymbol("AAPL"); + String str = entity.toString(); + assertTrue(str.contains("transactionId=1")); + assertTrue(str.contains("symbol='AAPL'")); + } +} diff --git a/src/test/java/org/hsbc/entity/WalletEntityTest.java b/src/test/java/org/hsbc/entity/WalletEntityTest.java new file mode 100644 index 0000000..0ae3d25 --- /dev/null +++ b/src/test/java/org/hsbc/entity/WalletEntityTest.java @@ -0,0 +1,40 @@ +package org.hsbc.entity; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class WalletEntityTest { + + @Test + void testNoArgsConstructor() { + WalletEntity entity = new WalletEntity(); + assertNotNull(entity); + } + + @Test + void testAllArgsConstructor() { + WalletEntity entity = new WalletEntity(1L, 5000.0); + assertEquals(1L, entity.getId()); + assertEquals(5000.0, entity.getBalance()); + } + + @Test + void testSettersAndGetters() { + WalletEntity entity = new WalletEntity(); + entity.setId(1L); + entity.setBalance(5000.0); + + assertEquals(1L, entity.getId()); + assertEquals(5000.0, entity.getBalance()); + } + + @Test + void testToString() { + WalletEntity entity = new WalletEntity(); + entity.setId(1L); + entity.setBalance(5000.0); + String str = entity.toString(); + assertTrue(str.contains("id=1")); + assertTrue(str.contains("balance=5000.0")); + } +} diff --git a/src/test/java/org/hsbc/exception/ExceptionClassesTest.java b/src/test/java/org/hsbc/exception/ExceptionClassesTest.java new file mode 100644 index 0000000..ea63d23 --- /dev/null +++ b/src/test/java/org/hsbc/exception/ExceptionClassesTest.java @@ -0,0 +1,77 @@ +package org.hsbc.exception; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class ExceptionClassesTest { + + @Test + void testResourceNotFoundException() { + ResourceNotFoundException ex = new ResourceNotFoundException("Not found"); + assertEquals("Not found", ex.getMessage()); + } + + @Test + void testInsufficientBalanceException() { + InsufficientBalanceException ex = new InsufficientBalanceException("Low balance"); + assertEquals("Low balance", ex.getMessage()); + } + + @Test + void testInvalidPmsIdException() { + InvalidPmsIdException ex1 = new InvalidPmsIdException(); + assertNull(ex1.getMessage()); + + InvalidPmsIdException ex2 = new InvalidPmsIdException("Invalid ID"); + assertEquals("Invalid ID", ex2.getMessage()); + + Throwable cause = new RuntimeException("Cause"); + InvalidPmsIdException ex3 = new InvalidPmsIdException(cause); + assertEquals(cause, ex3.getCause()); + + InvalidPmsIdException ex4 = new InvalidPmsIdException("Message", cause); + assertEquals("Message", ex4.getMessage()); + assertEquals(cause, ex4.getCause()); + + InvalidPmsIdException ex5 = new InvalidPmsIdException("Message", cause, true, true); + assertEquals("Message", ex5.getMessage()); + } + + @Test + void testInvalidTransactionIdException() { + InvalidTransactionIdException ex1 = new InvalidTransactionIdException(); + assertNull(ex1.getMessage()); + + InvalidTransactionIdException ex2 = new InvalidTransactionIdException("Invalid ID"); + assertEquals("Invalid ID", ex2.getMessage()); + + Throwable cause = new RuntimeException("Cause"); + InvalidTransactionIdException ex3 = new InvalidTransactionIdException(cause); + assertEquals(cause, ex3.getCause()); + + InvalidTransactionIdException ex4 = new InvalidTransactionIdException("Message", cause); + assertEquals("Message", ex4.getMessage()); + assertEquals(cause, ex4.getCause()); + + InvalidTransactionIdException ex5 = new InvalidTransactionIdException("Message", cause, true, true); + assertEquals("Message", ex5.getMessage()); + } + + @Test + void testErrorResponse() { + ErrorResponse er = new ErrorResponse(); + er.setStatus(404); + er.setError("Not Found"); + er.setMessage("Missing"); + er.setPath("/api/test"); + + assertEquals(404, er.getStatus()); + assertEquals("Not Found", er.getError()); + assertEquals("Missing", er.getMessage()); + assertEquals("/api/test", er.getPath()); + assertNotNull(er.getTimestamp()); // Default constructor sets timestamp now + + ErrorResponse er2 = new ErrorResponse(500, "Error", "Msg", "/path"); + assertEquals(500, er2.getStatus()); + } +} From 41d2066475abbe6a881248eb70ee59273fd888e5 Mon Sep 17 00:00:00 2001 From: dhananjay-k-s Date: Thu, 5 Feb 2026 16:43:35 +0000 Subject: [PATCH 4/4] adding import --- src/test/java/org/hsbc/controller/StockDataControllerTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/org/hsbc/controller/StockDataControllerTest.java b/src/test/java/org/hsbc/controller/StockDataControllerTest.java index c772fbe..5b493c7 100644 --- a/src/test/java/org/hsbc/controller/StockDataControllerTest.java +++ b/src/test/java/org/hsbc/controller/StockDataControllerTest.java @@ -12,8 +12,7 @@ import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;