|
|
@@ -1,40 +1,43 @@
|
|
|
package service;
|
|
|
|
|
|
-import com.danielbohry.stocks.App;
|
|
|
+import com.danielbohry.stocks.context.UserContext;
|
|
|
+import com.danielbohry.stocks.context.UserContextHolder;
|
|
|
import com.danielbohry.stocks.domain.Portfolio;
|
|
|
import com.danielbohry.stocks.domain.PortfolioStock;
|
|
|
+import com.danielbohry.stocks.exception.BadRequestException;
|
|
|
+import com.danielbohry.stocks.exception.NotFoundException;
|
|
|
+import com.danielbohry.stocks.exception.UnauthorizedException;
|
|
|
+import com.danielbohry.stocks.repository.portfolio.PortfolioEntity;
|
|
|
import com.danielbohry.stocks.repository.portfolio.PortfolioRepository;
|
|
|
-import com.danielbohry.stocks.repository.stock.StockRepository;
|
|
|
import com.danielbohry.stocks.service.ExchangeService;
|
|
|
+import com.danielbohry.stocks.service.ExchangeService.ExchangeRateResponse;
|
|
|
import com.danielbohry.stocks.service.portfolio.PortfolioEncryptService;
|
|
|
import com.danielbohry.stocks.service.portfolio.PortfolioService;
|
|
|
import com.danielbohry.stocks.service.stock.StockService;
|
|
|
-import org.junit.jupiter.api.AfterEach;
|
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
|
-import org.junit.jupiter.api.Disabled;
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
+import org.mockito.InjectMocks;
|
|
|
import org.mockito.Mock;
|
|
|
-import org.springframework.beans.factory.annotation.Autowired;
|
|
|
-import org.springframework.boot.test.context.SpringBootTest;
|
|
|
-import org.springframework.test.context.ContextConfiguration;
|
|
|
+import org.mockito.MockitoAnnotations;
|
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
import java.util.Optional;
|
|
|
|
|
|
+import static java.math.BigDecimal.TEN;
|
|
|
+import static java.math.BigDecimal.TWO;
|
|
|
+import static java.time.Instant.now;
|
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
|
-import static org.mockito.ArgumentMatchers.any;
|
|
|
-import static org.mockito.Mockito.when;
|
|
|
+import static org.mockito.Mockito.*;
|
|
|
|
|
|
-@Disabled
|
|
|
-@SpringBootTest
|
|
|
-@ContextConfiguration(classes = {App.class})
|
|
|
public class PortfolioServiceTest {
|
|
|
|
|
|
- @Autowired
|
|
|
- private PortfolioRepository portfolioRepository;
|
|
|
+ @Mock
|
|
|
+ private PortfolioRepository repository;
|
|
|
|
|
|
@Mock
|
|
|
- private StockRepository stockRepository;
|
|
|
+ private StockService stockService;
|
|
|
|
|
|
@Mock
|
|
|
private ExchangeService exchangeService;
|
|
|
@@ -42,69 +45,150 @@ public class PortfolioServiceTest {
|
|
|
@Mock
|
|
|
private PortfolioEncryptService encryptService;
|
|
|
|
|
|
+ @InjectMocks
|
|
|
private PortfolioService portfolioService;
|
|
|
|
|
|
+ private static final String APPLE_CODE = "AAPL";
|
|
|
+ private static final String APPLE_NAME = "Apple Inc.";
|
|
|
+
|
|
|
@BeforeEach
|
|
|
- public void setup() {
|
|
|
- StockService stockService = new StockService(stockRepository);
|
|
|
- portfolioService = new PortfolioService(portfolioRepository, stockService, exchangeService, encryptService);
|
|
|
+ void setUp() {
|
|
|
+ MockitoAnnotations.openMocks(this);
|
|
|
+ UserContextHolder.set(new UserContext("user-1", "user", List.of("USER")));
|
|
|
}
|
|
|
|
|
|
- @AfterEach
|
|
|
- public void teardown() {
|
|
|
- portfolioRepository.deleteAll();
|
|
|
+ @Test
|
|
|
+ void testGetAllIds_returnsIds() {
|
|
|
+ //given
|
|
|
+ List<String> ids = List.of("id1", "id2");
|
|
|
+ when(repository.findAllPortfolioIds()).thenReturn(ids);
|
|
|
+
|
|
|
+ //expect
|
|
|
+ assertEquals(ids, portfolioService.getAllIds());
|
|
|
}
|
|
|
|
|
|
@Test
|
|
|
- public void shouldCreateNewPortfolio() {
|
|
|
+ void testGetByUser_mapsPortfolios() {
|
|
|
+ //given
|
|
|
+ PortfolioEntity entity = PortfolioEntity.builder()
|
|
|
+ .id("p1")
|
|
|
+ .user("user-1")
|
|
|
+ .encryptedStocks("encrypted")
|
|
|
+ .build();
|
|
|
+
|
|
|
+ when(repository.findAllByUser("user-1")).thenReturn(List.of(entity));
|
|
|
+ when(repository.findById("p1")).thenReturn(Optional.of(entity));
|
|
|
+ when(encryptService.decryptStocks("encrypted")).thenReturn(List.of());
|
|
|
+ when(exchangeService.getCurrentRate("USD"))
|
|
|
+ .thenReturn(new ExchangeRateResponse("USD", "", Map.of("USD", BigDecimal.ONE)));
|
|
|
+
|
|
|
//when
|
|
|
- Portfolio result = portfolioService.create();
|
|
|
+ List<Portfolio> result = portfolioService.getByUser("user-1", "USD");
|
|
|
|
|
|
//then
|
|
|
- assertNotNull(result.getId());
|
|
|
+ assertEquals(1, result.size());
|
|
|
}
|
|
|
|
|
|
@Test
|
|
|
- public void shouldGetAPortfolio() {
|
|
|
+ void testGet_withMissingPortfolio_throws() {
|
|
|
//given
|
|
|
- Portfolio portfolio = portfolioService.create();
|
|
|
+ when(repository.findById("missing")).thenReturn(Optional.empty());
|
|
|
+
|
|
|
+ //expect
|
|
|
+ assertThrows(NotFoundException.class, () -> portfolioService.get("missing", "USD"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void testCreate_savesNewPortfolio() {
|
|
|
+ //given
|
|
|
+ PortfolioEntity saved = PortfolioEntity.builder()
|
|
|
+ .id("new-id").user("user-1")
|
|
|
+ .stocks(List.of())
|
|
|
+ .createdAt(now())
|
|
|
+ .updatedAt(now())
|
|
|
+ .build();
|
|
|
+
|
|
|
+ when(repository.save(any())).thenReturn(saved);
|
|
|
|
|
|
//when
|
|
|
- Portfolio result = portfolioService.get(portfolio.getId(), "USD");
|
|
|
+ Portfolio result = portfolioService.create();
|
|
|
|
|
|
//then
|
|
|
- assertNotNull(result.getId());
|
|
|
+ assertEquals("new-id", result.getId());
|
|
|
}
|
|
|
|
|
|
@Test
|
|
|
- public void shouldUpdateAPortfolio() {
|
|
|
+ void testUpdate_validStocks_saves() {
|
|
|
//given
|
|
|
- when(stockRepository.isValid(any())).thenReturn(true);
|
|
|
-
|
|
|
- Portfolio portfolio = portfolioService.create();
|
|
|
- PortfolioStock newStock = PortfolioStock.builder()
|
|
|
- .code("code")
|
|
|
- .quantity(3)
|
|
|
+ PortfolioEntity existing = PortfolioEntity.builder()
|
|
|
+ .id("pid").
|
|
|
+ user("user-1")
|
|
|
.build();
|
|
|
|
|
|
+ List<PortfolioStock> stocks = List.of(new PortfolioStock(APPLE_CODE, APPLE_NAME, 5, TWO, TEN));
|
|
|
+
|
|
|
+ when(repository.findById("pid")).thenReturn(Optional.of(existing));
|
|
|
+ when(stockService.isValid(APPLE_CODE)).thenReturn(true);
|
|
|
+ when(encryptService.encryptStocks(any())).thenReturn("encrypted");
|
|
|
+ when(repository.save(any())).thenReturn(existing);
|
|
|
+ when(encryptService.decryptStocks("encrypted")).thenReturn(List.of(new PortfolioEntity.PortfolioStock(APPLE_CODE, 5)));
|
|
|
+
|
|
|
//when
|
|
|
- Portfolio result = portfolioService.update(portfolio.getId(), List.of(newStock));
|
|
|
+ Portfolio updated = portfolioService.update("pid", stocks);
|
|
|
|
|
|
//then
|
|
|
- assertNotNull(result.getId());
|
|
|
- assertFalse(result.getStocks().isEmpty());
|
|
|
+ assertNotNull(updated);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void testUpdate_invalidUser_throwsUnauthorized() {
|
|
|
+ //given
|
|
|
+ PortfolioEntity existing = PortfolioEntity.builder()
|
|
|
+ .id("pid").user("another-user").build();
|
|
|
+
|
|
|
+ when(repository.findById("pid")).thenReturn(Optional.of(existing));
|
|
|
+
|
|
|
+ //expect
|
|
|
+ assertThrows(UnauthorizedException.class, () -> portfolioService.update("pid", List.of()));
|
|
|
}
|
|
|
|
|
|
@Test
|
|
|
- public void shouldDeleteAPortfolio() {
|
|
|
+ void testUpdate_withInvalidStock_throwsBadRequest() {
|
|
|
//given
|
|
|
- Portfolio portfolio = portfolioService.create();
|
|
|
+ PortfolioEntity existing = PortfolioEntity.builder()
|
|
|
+ .id("pid").user("user-1").build();
|
|
|
+
|
|
|
+ when(repository.findById("pid")).thenReturn(Optional.of(existing));
|
|
|
+ when(stockService.isValid("FAKE"))
|
|
|
+ .thenReturn(false);
|
|
|
+
|
|
|
+ //expect
|
|
|
+ assertThrows(BadRequestException.class, () ->
|
|
|
+ portfolioService.update("pid", List.of(new PortfolioStock("FAKE", "FAKE", 0, TWO, BigDecimal.ZERO))));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void testDelete_callsRepository() {
|
|
|
+ //when
|
|
|
+ portfolioService.delete("pid");
|
|
|
+
|
|
|
+ //expect
|
|
|
+ verify(repository).deleteById("pid");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void testCleanup_deletesEmptyEncrypted() {
|
|
|
+ //given
|
|
|
+ PortfolioEntity empty = PortfolioEntity.builder()
|
|
|
+ .id("pid").encryptedStocks("").build();
|
|
|
+
|
|
|
+ when(repository.findAll()).thenReturn(List.of(empty));
|
|
|
|
|
|
//when
|
|
|
- portfolioService.delete(portfolio.getId());
|
|
|
+ portfolioService.cleanup();
|
|
|
|
|
|
//then
|
|
|
- assertEquals(portfolioRepository.findById(portfolio.getId()), Optional.empty());
|
|
|
+ verify(repository).deleteById("pid");
|
|
|
}
|
|
|
|
|
|
}
|