package org.openlmis.restapi.service;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.openlmis.core.builder.FacilityBuilder;
import org.openlmis.core.builder.ProductBuilder;
import org.openlmis.core.domain.Facility;
import org.openlmis.core.domain.Product;
import org.openlmis.core.domain.StockAdjustmentReason;
import org.openlmis.core.exception.DataException;
import org.openlmis.core.repository.FacilityRepository;
import org.openlmis.core.repository.StockAdjustmentReasonRepository;
import org.openlmis.core.service.ProductService;
import org.openlmis.db.categories.UnitTests;
import org.openlmis.stockmanagement.builder.StockEventBuilder;
import org.openlmis.stockmanagement.domain.StockCard;
import org.openlmis.stockmanagement.domain.StockCardEntry;
import org.openlmis.stockmanagement.dto.StockEvent;
import org.openlmis.stockmanagement.service.StockCardService;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import static com.natpryce.makeiteasy.MakeItEasy.*;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import static org.mockito.Matchers.anyList;
import static org.mockito.Mockito.*;
@Category(UnitTests.class)
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(BlockJUnit4ClassRunner.class)
public class RestStockCardServiceTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@InjectMocks
private RestStockCardService restStockCardService;
@Mock
private FacilityRepository facilityRepository;
@Mock
private ProductService productService;
@Mock
private StockAdjustmentReasonRepository stockAdjustmentReasonRepository;
@Mock
private StockCardService stockCardService;
private Long facilityId;
private String productCode;
private List<StockEvent> stockEventList;
private Facility defaultFacility;
private Product defaultProduct;
private StockEvent stockEvent2;
private String reasonName;
private Long quantity;
private StockEvent stockEvent1;
private Long userId;
@Test
public void shouldThrowDataExceptionIfFacilityIdIsInvalid() throws Exception {
setupStockData();
expectedException.expect(DataException.class);
expectedException.expectMessage("error.facility.unknown");
when(facilityRepository.getById(facilityId)).thenReturn(null);
when(productService.getByCode(productCode)).thenReturn(defaultProduct);
restStockCardService.adjustStock(facilityId, stockEventList, userId);
}
@Test
public void shouldThrowDataExceptionIfInvalidAdjustment() throws Exception {
setupStockData();
expectedException.expect(DataException.class);
expectedException.expectMessage("error.stockmanagement.invalidadjustment");
when(facilityRepository.getById(facilityId)).thenReturn(defaultFacility);
when(productService.getByCode(productCode)).thenReturn(defaultProduct);
StockAdjustmentReason stockAdjustmentReason = new StockAdjustmentReason();
stockAdjustmentReason.setAdditive(true);
when(stockAdjustmentReasonRepository.getAdjustmentReasonByName("some reason")).thenReturn(stockAdjustmentReason);
stockEvent2.setReasonName(null);
StockCard expectedStockCard = StockCard.createZeroedStockCard(defaultFacility, defaultProduct);
when(stockCardService.getOrCreateStockCard(facilityId, productCode)).thenReturn(expectedStockCard);
restStockCardService.adjustStock(facilityId, stockEventList, userId);
}
@Test
public void shouldThrowDataExceptionIfProductIsInvalid() throws Exception {
setupStockData();
expectedException.expect(DataException.class);
expectedException.expectMessage("error.product.unknown");
when(facilityRepository.getById(facilityId)).thenReturn(defaultFacility);
when(productService.getByCode(productCode)).thenReturn(null);
restStockCardService.adjustStock(facilityId, stockEventList, userId);
}
@Test
public void shouldThrowDataExceptionIfAdjustmentReasonIsInvalid() throws Exception {
setupStockData();
expectedException.expect(DataException.class);
expectedException.expectMessage("error.stockadjustmentreason.unknown");
when(facilityRepository.getById(facilityId)).thenReturn(defaultFacility);
when(productService.getByCode(productCode)).thenReturn(defaultProduct);
String reasonName = "invalid reason";
stockEvent2.setReasonName(reasonName);
when(stockAdjustmentReasonRepository.getAdjustmentReasonByName(reasonName)).thenReturn(null);
restStockCardService.adjustStock(facilityId, stockEventList, userId);
}
@Test
public void shouldCreateOrUpdateStockCardIfAdjustmentIsValid() throws Exception {
setupStockData();
String productCode2 = "P2";
Product product2 = make(a(ProductBuilder.defaultProduct, with(ProductBuilder.code, productCode2)));
stockEvent2.setProductCode(productCode2);
when(facilityRepository.getById(facilityId)).thenReturn(defaultFacility);
when(productService.getByCode(productCode)).thenReturn(defaultProduct);
when(productService.getByCode(productCode2)).thenReturn(product2);
StockAdjustmentReason stockAdjustmentReason = new StockAdjustmentReason();
stockAdjustmentReason.setAdditive(true);
when(stockAdjustmentReasonRepository.getAdjustmentReasonByName(reasonName)).thenReturn(stockAdjustmentReason);
when(stockCardService.getOrCreateStockCard(facilityId, productCode)).thenReturn(StockCard.createZeroedStockCard(defaultFacility, defaultProduct));
when(stockCardService.getOrCreateStockCard(facilityId, productCode2)).thenReturn(StockCard.createZeroedStockCard(defaultFacility, product2));
restStockCardService.adjustStock(facilityId, stockEventList, userId);
verify(stockCardService).getOrCreateStockCard(facilityId, productCode);
verify(stockCardService).getOrCreateStockCard(facilityId, productCode2);
}
@Test
public void shouldAddStockCardEntriesWithAdjustmentReasonToStockCard() throws Exception {
setupStockData();
String stockAdjustmentReasonName = "Issue";
StockAdjustmentReason stockAdjustmentReason = new StockAdjustmentReason();
stockAdjustmentReason.setName(stockAdjustmentReasonName);
stockAdjustmentReason.setAdditive(false);
stockEvent1.setReasonName(stockAdjustmentReasonName);
String stockAdjustmentReasonName2 = "Acquire";
StockAdjustmentReason stockAdjustmentReason2 = new StockAdjustmentReason();
stockAdjustmentReason2.setName(stockAdjustmentReasonName2);
stockAdjustmentReason2.setAdditive(true);
stockEvent2.setReasonName(stockAdjustmentReasonName2);
when(facilityRepository.getById(facilityId)).thenReturn(defaultFacility);
when(productService.getByCode(productCode)).thenReturn(defaultProduct);
when(stockAdjustmentReasonRepository.getAdjustmentReasonByName(stockAdjustmentReasonName)).thenReturn(stockAdjustmentReason);
when(stockAdjustmentReasonRepository.getAdjustmentReasonByName(stockAdjustmentReasonName2)).thenReturn(stockAdjustmentReason2);
StockCard expectedStockCard = StockCard.createZeroedStockCard(defaultFacility, defaultProduct);
when(stockCardService.getOrCreateStockCard(facilityId, productCode)).thenReturn(expectedStockCard);
List<StockCardEntry> stockCardEntries = restStockCardService.adjustStock(facilityId, stockEventList, userId);
verify(stockCardService).addStockCardEntries(anyList());
assertThat(stockCardEntries.get(0).getAdjustmentReason(), is(stockAdjustmentReason));
assertThat(stockCardEntries.get(1).getAdjustmentReason(), is(stockAdjustmentReason2));
}
@Test
public void shouldAddStockCardEntriesWithPositiveQuantityToStockCardIfReasonIsAdditive() throws Exception {
setupStockData();
String stockAdjustmentReasonName = "Acquire";
StockAdjustmentReason stockAdjustmentReason = new StockAdjustmentReason();
stockAdjustmentReason.setName(stockAdjustmentReasonName);
stockAdjustmentReason.setAdditive(true);
stockEvent1.setReasonName(stockAdjustmentReasonName);
stockEvent2.setReasonName(stockAdjustmentReasonName);
Long quantity1 = 1000L;
Long quantity2 = 3000L;
stockEvent1.setQuantity(quantity1);
stockEvent2.setQuantity(quantity2);
when(facilityRepository.getById(facilityId)).thenReturn(defaultFacility);
when(productService.getByCode(productCode)).thenReturn(defaultProduct);
when(stockAdjustmentReasonRepository.getAdjustmentReasonByName(stockAdjustmentReasonName)).thenReturn(stockAdjustmentReason);
StockCard expectedStockCard = StockCard.createZeroedStockCard(defaultFacility, defaultProduct);
when(stockCardService.getOrCreateStockCard(facilityId, productCode)).thenReturn(expectedStockCard);
List<StockCardEntry> stockCardEntries = restStockCardService.adjustStock(facilityId, stockEventList, userId);
assertThat(stockCardEntries.get(0).getQuantity(), is(1000L));
assertThat(stockCardEntries.get(1).getQuantity(), is(3000L));
}
@Test
public void shouldAddStockCardEntriesWithPositiveQuantityToStockCardIfReasonIsNotAdditive() throws Exception {
setupStockData();
String stockAdjustmentReasonName = "Issue";
StockAdjustmentReason stockAdjustmentReason = new StockAdjustmentReason();
stockAdjustmentReason.setName(stockAdjustmentReasonName);
stockAdjustmentReason.setAdditive(false);
stockEvent1.setReasonName(stockAdjustmentReasonName);
stockEvent2.setReasonName(stockAdjustmentReasonName);
Long quantity1 = 1000L;
Long quantity2 = 3000L;
stockEvent1.setQuantity(quantity1);
stockEvent2.setQuantity(quantity2);
when(facilityRepository.getById(facilityId)).thenReturn(defaultFacility);
when(productService.getByCode(productCode)).thenReturn(defaultProduct);
when(stockAdjustmentReasonRepository.getAdjustmentReasonByName(stockAdjustmentReasonName)).thenReturn(stockAdjustmentReason);
StockCard expectedStockCard = StockCard.createZeroedStockCard(defaultFacility, defaultProduct);
when(stockCardService.getOrCreateStockCard(facilityId, productCode)).thenReturn(expectedStockCard);
List<StockCardEntry> stockCardEntries = restStockCardService.adjustStock(facilityId, stockEventList, userId);
assertThat(stockCardEntries.get(0).getQuantity(), is(-1000L));
assertThat(stockCardEntries.get(1).getQuantity(), is(-3000L));
}
@Test
public void shouldAddStockCardEntriesWithUserId() throws Exception {
setupStockData();
when(facilityRepository.getById(facilityId)).thenReturn(defaultFacility);
when(productService.getByCode(productCode)).thenReturn(defaultProduct);
StockAdjustmentReason stockAdjustmentReason = new StockAdjustmentReason();
stockAdjustmentReason.setAdditive(true);
when(stockAdjustmentReasonRepository.getAdjustmentReasonByName(reasonName)).thenReturn(stockAdjustmentReason);
StockCard expectedStockCard = StockCard.createZeroedStockCard(defaultFacility, defaultProduct);
when(stockCardService.getOrCreateStockCard(facilityId, productCode)).thenReturn(expectedStockCard);
List<StockCardEntry> stockCardEntries = restStockCardService.adjustStock(facilityId, stockEventList, userId);
assertThat(stockCardEntries.get(0).getCreatedBy(), is(userId));
assertThat(stockCardEntries.get(0).getModifiedBy(), is(userId));
}
@Test
public void shouldAddStockEventCustomPropsValues() throws Exception {
setupStockData();
HashMap<String, String> expirationDates = new HashMap<>();
expirationDates.put("expirationDates", "10/10/2016, 11/11/2016");
stockEvent1.setCustomProps(expirationDates);
when(facilityRepository.getById(facilityId)).thenReturn(defaultFacility);
when(productService.getByCode(productCode)).thenReturn(defaultProduct);
StockAdjustmentReason stockAdjustmentReason = new StockAdjustmentReason();
stockAdjustmentReason.setAdditive(true);
when(stockAdjustmentReasonRepository.getAdjustmentReasonByName(reasonName)).thenReturn(stockAdjustmentReason);
StockCard expectedStockCard = StockCard.createZeroedStockCard(defaultFacility, defaultProduct);
when(stockCardService.getOrCreateStockCard(facilityId, productCode)).thenReturn(expectedStockCard);
List<StockCardEntry> stockCardEntries = restStockCardService.adjustStock(facilityId, stockEventList, userId);
assertTrue(stockCardEntries.get(0).getKeyValues().get(0).getValueColumn().equals("10/10/2016, 11/11/2016"));
assertEquals(0, stockCardEntries.get(1).getKeyValues().size());
}
@Test
public void shouldSaveStockEventOccurredDateToStockCardEntry() {
setupStockData();
Date occurred1 = new Date();
stockEvent1.setOccurred(occurred1);
Date occurred2 = new Date();
stockEvent2.setOccurred(occurred2);
when(facilityRepository.getById(facilityId)).thenReturn(defaultFacility);
when(productService.getByCode(productCode)).thenReturn(defaultProduct);
StockAdjustmentReason stockAdjustmentReason = new StockAdjustmentReason();
stockAdjustmentReason.setAdditive(true);
when(stockAdjustmentReasonRepository.getAdjustmentReasonByName(reasonName)).thenReturn(stockAdjustmentReason);
StockCard expectedStockCard = StockCard.createZeroedStockCard(defaultFacility, defaultProduct);
when(stockCardService.getOrCreateStockCard(facilityId, productCode)).thenReturn(expectedStockCard);
List<StockCardEntry> stockCardEntries = restStockCardService.adjustStock(facilityId, stockEventList, userId);
assertTrue(stockCardEntries.get(0).getOccurred().equals(occurred1));
assertTrue(stockCardEntries.get(1).getOccurred().equals(occurred2));
}
@Test
public void shouldSaveStockEventDocumentNumberToStockCardEntry() {
setupStockData();
String referenceNumber1 = "123";
stockEvent1.setReferenceNumber(referenceNumber1);
String referenceNumber2 = "456";
stockEvent2.setReferenceNumber(referenceNumber2);
when(facilityRepository.getById(facilityId)).thenReturn(defaultFacility);
when(productService.getByCode(productCode)).thenReturn(defaultProduct);
StockAdjustmentReason stockAdjustmentReason = new StockAdjustmentReason();
stockAdjustmentReason.setAdditive(true);
when(stockAdjustmentReasonRepository.getAdjustmentReasonByName(reasonName)).thenReturn(stockAdjustmentReason);
StockCard expectedStockCard = StockCard.createZeroedStockCard(defaultFacility, defaultProduct);
when(stockCardService.getOrCreateStockCard(facilityId, productCode)).thenReturn(expectedStockCard);
List<StockCardEntry> stockCardEntries = restStockCardService.adjustStock(facilityId, stockEventList, userId);
assertTrue(stockCardEntries.get(0).getReferenceNumber().equals(referenceNumber1));
assertTrue(stockCardEntries.get(1).getReferenceNumber().equals(referenceNumber2));
}
@Test
public void shouldNotCreateNewStockCardReferenceWhenTheCardAlreadyExists() {
setupStockData();
when(facilityRepository.getById(facilityId)).thenReturn(defaultFacility);
when(productService.getByCode(productCode)).thenReturn(defaultProduct);
StockAdjustmentReason stockAdjustmentReason = new StockAdjustmentReason();
stockAdjustmentReason.setAdditive(true);
when(stockAdjustmentReasonRepository.getAdjustmentReasonByName(reasonName)).thenReturn(stockAdjustmentReason);
StockCard expectedStockCard = StockCard.createZeroedStockCard(defaultFacility, defaultProduct);
when(stockCardService.getOrCreateStockCard(facilityId, productCode)).thenReturn(expectedStockCard);
restStockCardService.adjustStock(facilityId, stockEventList, userId);
verify(stockCardService).getOrCreateStockCard(facilityId, productCode); //should only invoke once
}
private void setupStockData() {
facilityId = 1L;
productCode = "P123";
reasonName = "some reason";
quantity = 100L;
userId = 123L;
defaultFacility = make(a(FacilityBuilder.defaultFacility, with(FacilityBuilder.facilityId, facilityId)));
defaultProduct = make(a(ProductBuilder.defaultProduct, with(ProductBuilder.code, productCode)));
stockEventList = new ArrayList<>();
stockEvent1 = make(a(StockEventBuilder.defaultStockEvent,
with(StockEventBuilder.productCode, productCode),
with(StockEventBuilder.reasonName, reasonName),
with(StockEventBuilder.quantity, quantity)));
stockEventList.add(stockEvent1);
stockEvent2 = make(a(StockEventBuilder.defaultStockEvent,
with(StockEventBuilder.productCode, productCode),
with(StockEventBuilder.reasonName, reasonName),
with(StockEventBuilder.quantity, quantity)
));
stockEventList.add(stockEvent2);
}
}