BJ-917: Provide tests for PreparationFlowService

This commit is contained in:
a.romanov 2020-05-27 15:08:35 +03:00 committed by a.romanov
parent 6e01f45257
commit 468984cfe4
17 changed files with 365 additions and 77 deletions

View File

@ -135,6 +135,12 @@
<version>1.8.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>2.18.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@ -4,10 +4,15 @@ import com.rbkmoney.threeds.server.domain.root.rbkmoney.RBKMoneyPreparationReque
import com.rbkmoney.threeds.server.domain.root.rbkmoney.RBKMoneyPreparationResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientResponseException;
import org.springframework.web.client.RestTemplate;
@Service
@EnableRetry
@RequiredArgsConstructor
public class ThreeDsServerClient {
@ -16,6 +21,10 @@ public class ThreeDsServerClient {
@Value("${client.three-ds-server.url}")
private String url;
@Retryable(
value = RestClientResponseException.class,
backoff = @Backoff(delay = 60_000L),
maxAttempts = Integer.MAX_VALUE)
public RBKMoneyPreparationResponse preparationFlow(RBKMoneyPreparationRequest request) {
return restTemplate.postForEntity(
url,

View File

@ -0,0 +1,31 @@
package com.rbkmoney.threeds.server.storage.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.listener.RetryListenerSupport;
import java.util.List;
@Slf4j
@Configuration
public class RetryListenerConfig {
@Bean
public List<RetryListener> retryListeners() {
return List.of(new RetryListenerSupport() {
@Override
public <T, E extends Throwable> void onError(
RetryContext context, RetryCallback<T, E> callback,
Throwable throwable) {
log.warn("Retrying method={}, count={} because of the exception={}",
context.getAttribute("context.name"),
context.getRetryCount(),
throwable.toString());
}
});
}
}

View File

@ -4,7 +4,7 @@ import com.rbkmoney.damsel.three_ds_server_storage.*;
import com.rbkmoney.threeds.server.storage.entity.CardRangeEntity;
import com.rbkmoney.threeds.server.storage.mapper.CardRangeMapper;
import com.rbkmoney.threeds.server.storage.repository.CardRangeRepository;
import com.rbkmoney.threeds.server.storage.service.RBKMoneyPreparationFlowService;
import com.rbkmoney.threeds.server.storage.service.PreparationFlowService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@ -16,7 +16,7 @@ import static java.util.stream.Collectors.toList;
@RequiredArgsConstructor
public class CardRangesStorageHandler implements CardRangesStorageSrv.Iface {
private final RBKMoneyPreparationFlowService preparationFlowService;
private final PreparationFlowService preparationFlowService;
private final CardRangeRepository repository;
private final CardRangeMapper mapper;

View File

@ -0,0 +1,15 @@
package com.rbkmoney.threeds.server.storage.mapper;
import com.rbkmoney.threeds.server.storage.entity.SerialNumEntity;
import org.springframework.stereotype.Service;
@Service
public class SerialNumMapper {
public SerialNumEntity toEntity(String serialNum, String providerId) {
return SerialNumEntity.builder()
.providerId(providerId)
.serialNum(serialNum)
.build();
}
}

View File

@ -1,23 +0,0 @@
package com.rbkmoney.threeds.server.storage.service;
import com.rbkmoney.threeds.server.domain.root.rbkmoney.RBKMoneyPreparationResponse;
import com.rbkmoney.threeds.server.storage.mapper.CardRangeMapper;
import com.rbkmoney.threeds.server.storage.repository.CardRangeRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class CardRangeUpdater {
private final CardRangeRepository repository;
private final CardRangeMapper mapper;
public void update(RBKMoneyPreparationResponse response) {
String providerId = response.getProviderId();
repository.saveAll(mapper.toEntities(response.getAddedCardRanges(), providerId));
repository.saveAll(mapper.toEntities(response.getModifiedCardRanges(), providerId));
repository.deleteAll(mapper.toEntities(response.getDeletedCardRanges(), providerId));
}
}

View File

@ -0,0 +1,40 @@
package com.rbkmoney.threeds.server.storage.service;
import com.rbkmoney.threeds.server.domain.root.rbkmoney.RBKMoneyPreparationResponse;
import com.rbkmoney.threeds.server.storage.entity.SerialNumEntity;
import com.rbkmoney.threeds.server.storage.mapper.CardRangeMapper;
import com.rbkmoney.threeds.server.storage.mapper.SerialNumMapper;
import com.rbkmoney.threeds.server.storage.repository.CardRangeRepository;
import com.rbkmoney.threeds.server.storage.repository.SerialNumRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
@Service
@RequiredArgsConstructor
public class PreparationFlowDataUpdater {
private final SerialNumRepository serialNumRepository;
private final SerialNumMapper serialNumMapper;
private final CardRangeRepository cardRangeRepository;
private final CardRangeMapper cardRangeMapper;
public String getCurrentSerialNum(String providerId) {
return serialNumRepository
.findByProviderId(providerId)
.map(SerialNumEntity::getSerialNum)
.orElse(null);
}
@Transactional
public void update(RBKMoneyPreparationResponse response) {
String providerId = response.getProviderId();
String serialNum = response.getSerialNum();
cardRangeRepository.saveAll(cardRangeMapper.toEntities(response.getAddedCardRanges(), providerId));
cardRangeRepository.saveAll(cardRangeMapper.toEntities(response.getModifiedCardRanges(), providerId));
cardRangeRepository.deleteAll(cardRangeMapper.toEntities(response.getDeletedCardRanges(), providerId));
serialNumRepository.save(serialNumMapper.toEntity(serialNum, providerId));
}
}

View File

@ -4,30 +4,19 @@ import com.rbkmoney.threeds.server.domain.root.rbkmoney.RBKMoneyPreparationReque
import com.rbkmoney.threeds.server.domain.root.rbkmoney.RBKMoneyPreparationResponse;
import com.rbkmoney.threeds.server.storage.client.ThreeDsServerClient;
import lombok.RequiredArgsConstructor;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientResponseException;
import javax.transaction.Transactional;
@Service
@EnableRetry
@RequiredArgsConstructor
public class RBKMoneyPreparationFlowService {
public class PreparationFlowService {
private final ThreeDsServerClient client;
private final SerialNumUpdater serialNumUpdater;
private final CardRangeUpdater cardRangeUpdater;
private final PreparationFlowDataUpdater dataUpdater;
@Transactional
@Retryable(
value = RestClientResponseException.class,
backoff = @Backoff(delay = 60_000L),
maxAttempts = Integer.MAX_VALUE)
public void init(String providerId) {
String serialNum = serialNumUpdater.getCurrent(providerId);
String serialNum = dataUpdater.getCurrentSerialNum(providerId);
RBKMoneyPreparationRequest request = RBKMoneyPreparationRequest.builder()
.providerId(providerId)
@ -36,7 +25,6 @@ public class RBKMoneyPreparationFlowService {
RBKMoneyPreparationResponse response = client.preparationFlow(request);
serialNumUpdater.update(response);
cardRangeUpdater.update(response);
dataUpdater.update(response);
}
}

View File

@ -1,31 +0,0 @@
package com.rbkmoney.threeds.server.storage.service;
import com.rbkmoney.threeds.server.domain.root.rbkmoney.RBKMoneyPreparationResponse;
import com.rbkmoney.threeds.server.storage.entity.SerialNumEntity;
import com.rbkmoney.threeds.server.storage.repository.SerialNumRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class SerialNumUpdater {
private final SerialNumRepository repository;
public String getCurrent(String providerId) {
return repository
.findByProviderId(providerId)
.map(SerialNumEntity::getSerialNum)
.orElse(null);
}
public void update(RBKMoneyPreparationResponse response) {
String updatedSerialNum = response.getSerialNum();
if (updatedSerialNum == null) return;
repository.save(SerialNumEntity.builder()
.providerId(response.getProviderId())
.serialNum(updatedSerialNum)
.build());
}
}

View File

@ -0,0 +1,16 @@
package com.rbkmoney.threeds.server.storage.config;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.web.client.RestTemplate;
@TestConfiguration
public class TestConfig {
@Bean
@Primary
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}

View File

@ -14,7 +14,7 @@ import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
public class CardRangesRepositoryTest extends AbstractRepositoryTest {
public class CardRangesRepositoryTest extends PostgresRepositoryTest {
@Autowired
private CardRangeRepository repository;

View File

@ -15,7 +15,7 @@ import static org.junit.Assert.assertTrue;
@RunWith(SpringRunner.class)
public class ChallengeFlowTransactionInfoRepositoryTest extends AbstractRepositoryTest {
public class ChallengeFlowTransactionInfoRepositoryTest extends PostgresRepositoryTest {
@Autowired
private ChallengeFlowTransactionInfoRepository repository;

View File

@ -1,6 +1,7 @@
package com.rbkmoney.threeds.server.storage.repository;
import com.rbkmoney.threeds.server.storage.ThreeDsServerStorageApplication;
import com.rbkmoney.threeds.server.storage.config.TestConfig;
import org.junit.ClassRule;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.util.TestPropertyValues;
@ -16,8 +17,10 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
@SpringBootTest(webEnvironment = RANDOM_PORT)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
@ContextConfiguration(classes = ThreeDsServerStorageApplication.class, initializers = AbstractRepositoryTest.Initializer.class)
public class AbstractRepositoryTest {
@ContextConfiguration(
classes = {ThreeDsServerStorageApplication.class, TestConfig.class},
initializers = PostgresRepositoryTest.Initializer.class)
public class PostgresRepositoryTest {
@ClassRule
@SuppressWarnings("rawtypes")

View File

@ -13,7 +13,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertTrue;
@RunWith(SpringRunner.class)
public class SerialNumRepositoryTest extends AbstractRepositoryTest {
public class SerialNumRepositoryTest extends PostgresRepositoryTest {
@Autowired
private SerialNumRepository repository;

View File

@ -0,0 +1,114 @@
package com.rbkmoney.threeds.server.storage.service;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import com.google.common.base.Charsets;
import com.rbkmoney.threeds.server.storage.entity.CardRangeEntity;
import com.rbkmoney.threeds.server.storage.entity.SerialNumEntity;
import com.rbkmoney.threeds.server.storage.repository.CardRangeRepository;
import com.rbkmoney.threeds.server.storage.repository.PostgresRepositoryTest;
import com.rbkmoney.threeds.server.storage.repository.SerialNumRepository;
import io.micrometer.core.instrument.util.IOUtils;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertTrue;
@RunWith(SpringRunner.class)
@TestPropertySource(properties = "client.three-ds-server.url=http://127.0.0.1:8089/")
public class PreparationFlowServiceIT extends PostgresRepositoryTest {
@ClassRule
public static WireMockRule wireMockRule = new WireMockRule(8089);
@Autowired
private ResourceLoader resourceLoader;
@Autowired
private SerialNumRepository serialNumRepository;
@Autowired
private CardRangeRepository cardRangeRepository;
@Autowired
private PreparationFlowService preparationFlowService;
@Test
public void shouldSaveUpdatedSerialNumAndCardRanges() throws IOException {
// Given
stubForOkResponse();
// When
preparationFlowService.init("1");
// Then
Optional<SerialNumEntity> serialNum = serialNumRepository.findByProviderId("1");
assertTrue(serialNum.isPresent());
assertThat(serialNum.get().getSerialNum()).isEqualTo("20190411083623719000");
List<CardRangeEntity> cardRanges = cardRangeRepository.findByPkProviderId("1");
assertThat(cardRanges).hasSize(20);
}
@Test
public void shouldRetryOnException() throws IOException {
// Given
stubWithErrorAndThenOkResponse();
// When
preparationFlowService.init("2");
// Then
Optional<SerialNumEntity> serialNum = serialNumRepository.findByProviderId("2");
assertTrue(serialNum.isPresent());
assertThat(serialNum.get().getSerialNum()).isEqualTo("20190411083623719000");
List<CardRangeEntity> cardRanges = cardRangeRepository.findByPkProviderId("2");
assertThat(cardRanges).hasSize(3);
}
private void stubForOkResponse() throws IOException {
stubFor(post(urlEqualTo("/"))
.willReturn(aResponse()
.withStatus(HttpStatus.OK.value())
.withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.withBody(readStringFromFile("preparation-flow-response-1.json"))));
}
private void stubWithErrorAndThenOkResponse() throws IOException {
stubFor(post(urlEqualTo("/"))
.inScenario("preparation")
.whenScenarioStateIs(STARTED)
.willReturn(aResponse()
.withStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()))
.willSetStateTo("OK"));
stubFor(post(urlEqualTo("/"))
.inScenario("preparation")
.whenScenarioStateIs("OK")
.willReturn(aResponse()
.withStatus(HttpStatus.OK.value())
.withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.withBody(readStringFromFile("preparation-flow-response-2.json"))));
}
private String readStringFromFile(String fileName) throws IOException {
return IOUtils.toString(
resourceLoader.getResource("classpath:__files/" + fileName).getInputStream(),
Charsets.UTF_8);
}
}

View File

@ -0,0 +1,99 @@
{
"messageType": "RBKMONEY_PREPARATION_RESPONSE",
"providerId": "1",
"serialNum": "20190411083623719000",
"addedCardRanges": [
{
"startRange": "6543200100000",
"endRange": "6543200199999"
},
{
"startRange": "65432102000000",
"endRange": "65432102999999"
},
{
"startRange": "7654310400000000",
"endRange": "7654310499999999"
},
{
"startRange": "7654340600000000",
"endRange": "7654340699999999"
},
{
"startRange": "7654350700000000",
"endRange": "7654350799999999"
},
{
"startRange": "7654360800000000",
"endRange": "7654360899999999"
},
{
"startRange": "7654370900000000",
"endRange": "7654370999999999"
},
{
"startRange": "7654381000000000",
"endRange": "7654381099999999"
},
{
"startRange": "7654391100000000",
"endRange": "7654391199999999"
},
{
"startRange": "8765411200000000",
"endRange": "8765411299999999"
},
{
"startRange": "8765431400000000",
"endRange": "8765431499999999"
},
{
"startRange": "8765441500000000",
"endRange": "8765441599999999"
},
{
"startRange": "8765451600000000",
"endRange": "8765451699999999"
},
{
"startRange": "8765461700000000",
"endRange": "8765461799999999"
},
{
"startRange": "8765471800000000",
"endRange": "8765471899999999"
},
{
"startRange": "8765481900000000",
"endRange": "8765481999999999"
},
{
"startRange": "8765492000000000",
"endRange": "8765492099999999"
},
{
"startRange": "1876542400000000000",
"endRange": "1876542499999999999"
},
{
"startRange": "8765422500000000",
"endRange": "8765422599999999"
}
],
"modifiedCardRanges": [
{
"startRange": "9876512600000000",
"endRange": "9876512699999999"
},
{
"startRange": "765430270000000",
"endRange": "765430279999999"
}
],
"deletedCardRanges": [
{
"startRange": "765430270000000",
"endRange": "765430279999999"
}
]
}

View File

@ -0,0 +1,21 @@
{
"messageType": "RBKMONEY_PREPARATION_RESPONSE",
"providerId": "2",
"serialNum": "20190411083623719000",
"addedCardRanges": [
{
"startRange": "6543200100000",
"endRange": "6543200199999"
},
{
"startRange": "65432102000000",
"endRange": "65432102999999"
},
{
"startRange": "7654310400000000",
"endRange": "7654310499999999"
}
],
"modifiedCardRanges": [],
"deletedCardRanges": []
}