PortfolioServiceTest.java 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. package service;
  2. import com.danielbohry.stocks.context.UserContext;
  3. import com.danielbohry.stocks.context.UserContextHolder;
  4. import com.danielbohry.stocks.domain.Portfolio;
  5. import com.danielbohry.stocks.domain.PortfolioStock;
  6. import com.danielbohry.stocks.exception.BadRequestException;
  7. import com.danielbohry.stocks.exception.NotFoundException;
  8. import com.danielbohry.stocks.exception.UnauthorizedException;
  9. import com.danielbohry.stocks.repository.portfolio.PortfolioEntity;
  10. import com.danielbohry.stocks.repository.portfolio.PortfolioRepository;
  11. import com.danielbohry.stocks.service.ExchangeService;
  12. import com.danielbohry.stocks.service.ExchangeService.ExchangeRateResponse;
  13. import com.danielbohry.stocks.service.portfolio.PortfolioEncryptService;
  14. import com.danielbohry.stocks.service.portfolio.PortfolioService;
  15. import com.danielbohry.stocks.service.stock.StockService;
  16. import org.junit.jupiter.api.BeforeEach;
  17. import org.junit.jupiter.api.Test;
  18. import org.mockito.InjectMocks;
  19. import org.mockito.Mock;
  20. import org.mockito.MockitoAnnotations;
  21. import java.math.BigDecimal;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.Optional;
  25. import static java.math.BigDecimal.TEN;
  26. import static java.math.BigDecimal.TWO;
  27. import static java.time.Instant.now;
  28. import static org.junit.jupiter.api.Assertions.*;
  29. import static org.mockito.Mockito.*;
  30. public class PortfolioServiceTest {
  31. @Mock
  32. private PortfolioRepository repository;
  33. @Mock
  34. private StockService stockService;
  35. @Mock
  36. private ExchangeService exchangeService;
  37. @Mock
  38. private PortfolioEncryptService encryptService;
  39. @InjectMocks
  40. private PortfolioService portfolioService;
  41. private static final String APPLE_CODE = "AAPL";
  42. private static final String APPLE_NAME = "Apple Inc.";
  43. @BeforeEach
  44. void setUp() {
  45. MockitoAnnotations.openMocks(this);
  46. UserContextHolder.set(new UserContext("user-1", "user", List.of("USER")));
  47. }
  48. @Test
  49. void testGetAllIds_returnsIds() {
  50. //given
  51. List<String> ids = List.of("id1", "id2");
  52. when(repository.findAllPortfolioIds()).thenReturn(ids);
  53. //expect
  54. assertEquals(ids, portfolioService.getAllIds());
  55. }
  56. @Test
  57. void testGetByUser_mapsPortfolios() {
  58. //given
  59. PortfolioEntity entity = PortfolioEntity.builder()
  60. .id("p1")
  61. .user("user-1")
  62. .encryptedStocks("encrypted")
  63. .build();
  64. when(repository.findAllByUser("user-1")).thenReturn(List.of(entity));
  65. when(repository.findById("p1")).thenReturn(Optional.of(entity));
  66. when(encryptService.decryptStocks("encrypted")).thenReturn(List.of());
  67. when(exchangeService.getCurrentRate("USD"))
  68. .thenReturn(new ExchangeRateResponse("USD", "", Map.of("USD", BigDecimal.ONE)));
  69. //when
  70. List<Portfolio> result = portfolioService.getByUser("user-1", "USD");
  71. //then
  72. assertEquals(1, result.size());
  73. }
  74. @Test
  75. void testGet_withMissingPortfolio_throws() {
  76. //given
  77. when(repository.findById("missing")).thenReturn(Optional.empty());
  78. //expect
  79. assertThrows(NotFoundException.class, () -> portfolioService.get("missing", "USD"));
  80. }
  81. @Test
  82. void testCreate_savesNewPortfolio() {
  83. //given
  84. PortfolioEntity saved = PortfolioEntity.builder()
  85. .id("new-id").user("user-1")
  86. .stocks(List.of())
  87. .createdAt(now())
  88. .updatedAt(now())
  89. .build();
  90. when(repository.save(any())).thenReturn(saved);
  91. //when
  92. Portfolio result = portfolioService.create();
  93. //then
  94. assertEquals("new-id", result.getId());
  95. }
  96. @Test
  97. void testUpdate_validStocks_saves() {
  98. //given
  99. PortfolioEntity existing = PortfolioEntity.builder()
  100. .id("pid").
  101. user("user-1")
  102. .build();
  103. List<PortfolioStock> stocks = List.of(new PortfolioStock(APPLE_CODE, APPLE_NAME, 5, TWO, TEN));
  104. when(repository.findById("pid")).thenReturn(Optional.of(existing));
  105. when(stockService.isValid(APPLE_CODE)).thenReturn(true);
  106. when(encryptService.encryptStocks(any())).thenReturn("encrypted");
  107. when(repository.save(any())).thenReturn(existing);
  108. when(encryptService.decryptStocks("encrypted")).thenReturn(List.of(new PortfolioEntity.PortfolioStock(APPLE_CODE, 5)));
  109. //when
  110. Portfolio updated = portfolioService.update("pid", stocks);
  111. //then
  112. assertNotNull(updated);
  113. }
  114. @Test
  115. void testUpdate_invalidUser_throwsUnauthorized() {
  116. //given
  117. PortfolioEntity existing = PortfolioEntity.builder()
  118. .id("pid").user("another-user").build();
  119. when(repository.findById("pid")).thenReturn(Optional.of(existing));
  120. //expect
  121. assertThrows(UnauthorizedException.class, () -> portfolioService.update("pid", List.of()));
  122. }
  123. @Test
  124. void testUpdate_withInvalidStock_throwsBadRequest() {
  125. //given
  126. PortfolioEntity existing = PortfolioEntity.builder()
  127. .id("pid").user("user-1").build();
  128. when(repository.findById("pid")).thenReturn(Optional.of(existing));
  129. when(stockService.isValid("FAKE"))
  130. .thenReturn(false);
  131. //expect
  132. assertThrows(BadRequestException.class, () ->
  133. portfolioService.update("pid", List.of(new PortfolioStock("FAKE", "FAKE", 0, TWO, BigDecimal.ZERO))));
  134. }
  135. @Test
  136. void testDelete_callsRepository() {
  137. //when
  138. portfolioService.delete("pid");
  139. //expect
  140. verify(repository).deleteById("pid");
  141. }
  142. @Test
  143. void testCleanup_deletesEmptyEncrypted() {
  144. //given
  145. PortfolioEntity empty = PortfolioEntity.builder()
  146. .id("pid").encryptedStocks("").build();
  147. when(repository.findAll()).thenReturn(List.of(empty));
  148. //when
  149. portfolioService.cleanup();
  150. //then
  151. verify(repository).deleteById("pid");
  152. }
  153. }