diff --git a/clearing-parent/account-service/pom.xml b/clearing-parent/account-service/pom.xml index 7585732cf..ddbf3d9d1 100644 --- a/clearing-parent/account-service/pom.xml +++ b/clearing-parent/account-service/pom.xml @@ -36,6 +36,27 @@ com.fasterxml.jackson.core jackson-databind + + + org.springframework + spring-test + test + + + org.mockito + mockito-core + + + org.junit.jupiter + junit-jupiter + test + + + org.assertj + assertj-core + test + + @@ -63,6 +84,23 @@ ${project.artifactId} + + org.apache.maven.plugins + maven-surefire-plugin + 2.21.0 + + + org.junit.platform + junit-platform-surefire-provider + 1.2.0-M1 + + + org.junit.jupiter + junit-jupiter-engine + 5.2.0-M1 + + + diff --git a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/BankAccountService.java b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/BankAccountService.java index 1293f4d9d..3c1ad9ed6 100644 --- a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/BankAccountService.java +++ b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/BankAccountService.java @@ -55,6 +55,7 @@ public class BankAccountService extends QueueConsumer implements InitializingBea bankAccount.setDestination(req.getDestination()); bankAccount.setTaxpayerIdentificationNumber(req.getTaxpayerIdentificationNumber()); bankAccount.setTaxRegistrationReasonCode(req.getTaxRegistrationReasonCode()); + bankAccount.setAccount(req.getAccount()); bankAccountMap.insert(bankAccount); log.debug("successfully processed, new id {}", bankAccount.getId()); @@ -72,14 +73,19 @@ public class BankAccountService extends QueueConsumer implements InitializingBea bankAccount.setDestination(req.getDestination()); bankAccount.setTaxpayerIdentificationNumber(req.getTaxpayerIdentificationNumber()); bankAccount.setTaxRegistrationReasonCode(req.getTaxRegistrationReasonCode()); + bankAccount.setAccount(req.getAccount()); + bankAccountMap.update(bankAccount); + log.debug("successfully update, existing bankAccount with id {}", bankAccount.getId()); } private void bankAccountDelete(BaseRequest userRequest) { CommonDeleteRequest req = userRequest.getRequestPayload(); log.debug("CommonDeleteRequest received id = {}", req.getId()); BankAccount bankAccount = bankAccountMap.getSingleObjectByID(req.getId()); + bankAccountMap.delete(bankAccount); + log.debug("successfully delete, existing bankAccount with id {}", bankAccount.getId()); } } diff --git a/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/config/HazelcastServiceTestConfiguration.java b/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/config/HazelcastServiceTestConfiguration.java new file mode 100644 index 000000000..60f94af16 --- /dev/null +++ b/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/config/HazelcastServiceTestConfiguration.java @@ -0,0 +1,68 @@ +package ru.spcex.clearing.account.config; + +import com.hazelcast.config.*; +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import ru.spcex.platform.imdg.iml.hazelcast.config.HazelcastClientParams; +import ru.spcex.platform.imdg.iml.hazelcast.service.HazelcastService; +import ru.spcex.platform.imdg.iml.hazelcast.util.HazelcastHelper; + +import java.util.List; +import java.util.Random; + +@Configuration +public class HazelcastServiceTestConfiguration { + private HazelcastInstance hazelcastInstance; + + private static ThreadPoolTaskExecutor createThreadPoolTaskExecutor(int maxPoolSz, boolean waitForCompletion) { + ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor(); + if (maxPoolSz > 2) { + pool.setKeepAliveSeconds(60); + pool.setAllowCoreThreadTimeOut(true); + } + pool.setCorePoolSize(maxPoolSz); + pool.setWaitForTasksToCompleteOnShutdown(waitForCompletion); + return pool; + } + + @Bean(name = "hazelcastServiceTest") + public HazelcastService hazelcastService(@Qualifier("taskExecutorHazelcastClientInitializer") ThreadPoolTaskExecutor taskExecutorHazelcastClientInitializer, @Qualifier("taskExecutorIdGeneratorAwaiter") ThreadPoolTaskExecutor taskExecutorIdGeneratorAwaiter, HazelcastClientParams params) { + Config cfg = new Config(); + cfg.setInstanceName("localhost"); + + NetworkConfig networkConfig = new NetworkConfig(); + JoinConfig joinConfig = new JoinConfig(); + joinConfig.setMulticastConfig(new MulticastConfig().setEnabled(false)); + joinConfig.setTcpIpConfig(new TcpIpConfig().setEnabled(true).setMembers(List.of("127.0.0.1"))); + networkConfig.setJoin(joinConfig); + cfg.setNetworkConfig(networkConfig); + hazelcastInstance = Hazelcast.newHazelcastInstance(cfg); + HazelcastHelper.otcSystem_setStorageState(true, hazelcastInstance); + return new HazelcastService(taskExecutorHazelcastClientInitializer, taskExecutorIdGeneratorAwaiter, params); + } + + @Bean(name = "taskExecutorHazelcastClientInitializer") + public ThreadPoolTaskExecutor taskExecutorHazelcastClientInitializer() { + return createThreadPoolTaskExecutor(1, true); + } + + @Bean(name = "taskExecutorIdGeneratorAwaiter") + public ThreadPoolTaskExecutor taskExecutorIdGeneratorAwaiter() { + return createThreadPoolTaskExecutor(1, false); + } + + @Bean(name = "hazelcastClientParams") + public HazelcastClientParams getHazelcastClientParams() { + HazelcastClientParams params = new HazelcastClientParams(); + params.setLogin("dev"); + params.setPassword("dev-pass"); + params.setClusterMembers("127.0.0.1"); + params.setInstanceName("hzTestClient" + new Random().nextInt()); + params.setNearCacheConfig(new NearCacheConfig()); + return params; + } +} diff --git a/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/service/BankAccountServiceTest.java b/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/service/BankAccountServiceTest.java new file mode 100644 index 000000000..cbc7952d1 --- /dev/null +++ b/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/service/BankAccountServiceTest.java @@ -0,0 +1,332 @@ +package ru.spcex.clearing.account.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.hazelcast.core.IMap; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.MockConsumer; +import org.apache.kafka.clients.consumer.OffsetResetStrategy; +import org.apache.kafka.common.TopicPartition; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import ru.clearing.classes.statics.data.account.BankAccount; +import ru.spcex.clearing.account.config.HazelcastServiceTestConfiguration; +import ru.spcex.clearing.account.utils.MatcherFactory.Matcher; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.ActionType; +import ru.spcex.clearing.platform.messaging.domain.BaseRequest; +import ru.spcex.clearing.platform.messaging.domain.Consts; +import ru.spcex.clearing.platform.messaging.domain.cud.account.BankAccountNewRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.account.BankAccountUpdateRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonDeleteRequest; +import ru.spcex.platform.imdg.iml.hazelcast.service.HazelcastService; + +import java.util.Collections; +import java.util.HashMap; + +import static ru.spcex.clearing.account.utils.MatcherFactory.usingIgnoringFieldsComparator; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + HazelcastServiceTestConfiguration.class}) +public class BankAccountServiceTest { + public static final Matcher BANK_ACCOUNT_MATCHER = usingIgnoringFieldsComparator("id"); + private static final int PARTITION = 0; + private static final String TOPIC_ACCOUNT_NEW = Consts.DESTINATION_BANK_ACCOUNT_NEW; + private static final String TOPIC_ACCOUNT_UPDATE = Consts.DESTINATION_BANK_ACCOUNT_UPDATE; + private static final String TOPIC_ACCOUNT_DELETE = Consts.DESTINATION_BANK_ACCOUNT_DELETE; + private static Long currentId = 0L; + + @Autowired + @Qualifier("hazelcastServiceTest") + private HazelcastService hazelcastServiceTest; + private MockConsumer mockConsumer; + + @BeforeEach + void setUp() { + mockConsumer = new MockConsumer<>(OffsetResetStrategy.EARLIEST); + } + + /** + * {@link BankAccountService#bankAccountNew(BaseRequest request)}
+ * Тест проверяет генерацию сущности {@link BankAccount} в Hazelcast при передаче из Apache Kafka.
+ * Входной запрос {@link BankAccountNewRequest}:
+ * {@link BankAccountNewRequest#bankIdentificationCode} - 99999
+ * {@link BankAccountNewRequest#bankName} - ооо тинькофф
+ * {@link BankAccountNewRequest#correspondentAccount} - 9294189285498598598
+ * {@link BankAccountNewRequest#correspondentAccountName} - BIK OF
+ * {@link BankAccountNewRequest#currency} - RUB
+ * {@link BankAccountNewRequest#destination} - OOO ROGA I KOPITA
+ * {@link BankAccountNewRequest#taxpayerIdentificationNumber} - 848484848484
+ * {@link BankAccountNewRequest#taxRegistrationReasonCode} - 886886
+ * {@link BankAccountNewRequest#account} - 123456789123
+ */ + @Test + public void bankAccountNew() throws InterruptedException { + //arrange + BankAccountNewRequest bankAccountNewRequest = new BankAccountNewRequest(); + bankAccountNewRequest.setBankName("ooo tinkoff"); + bankAccountNewRequest.setBankIdentificationCode("99999"); + bankAccountNewRequest.setCorrespondentAccount("9294189285498598598"); + bankAccountNewRequest.setCorrespondentAccountName("BIK OF TINKOFF"); + bankAccountNewRequest.setCurrency("RUB"); + bankAccountNewRequest.setDestination("OOO ROGA I KOPITA"); + bankAccountNewRequest.setTaxpayerIdentificationNumber("848484848484"); + bankAccountNewRequest.setTaxRegistrationReasonCode("886886"); + bankAccountNewRequest.setAccount("123456789123"); + + BaseRequest baseNewRequest = new BaseRequest<>(); + baseNewRequest.setRequestPayload(bankAccountNewRequest); + baseNewRequest.setId(currentId); + baseNewRequest.setActionType(ActionType.NEW); + String jsonBaseNewRequest; + ObjectMapper objectMapper = new ObjectMapper(); + try { + jsonBaseNewRequest = objectMapper.writeValueAsString(baseNewRequest); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + BankAccount predictableResult = new BankAccount(); + predictableResult.setBankName("ooo tinkoff"); + predictableResult.setBankIdentificationCode("99999"); + predictableResult.setCorrespondentAccount("9294189285498598598"); + predictableResult.setCorrespondentAccountName("BIK OF TINKOFF"); + predictableResult.setCurrency("RUB"); + predictableResult.setDestination("OOO ROGA I KOPITA"); + predictableResult.setTaxpayerIdentificationNumber("848484848484"); + predictableResult.setTaxRegistrationReasonCode("886886"); + predictableResult.setAccount("123456789123"); + predictableResult.setId(currentId); + //KAFKA + mockConsumer.schedulePollTask(() -> { + mockConsumer.rebalance(Collections.singletonList(new TopicPartition(TOPIC_ACCOUNT_NEW, PARTITION))); + mockConsumer.addRecord(new ConsumerRecord<>(TOPIC_ACCOUNT_NEW, PARTITION, 0, "key", jsonBaseNewRequest)); + }); + + + HashMap startOffsets = new HashMap<>(); + TopicPartition tp = new TopicPartition(TOPIC_ACCOUNT_NEW, PARTITION); + startOffsets.put(tp, 0L); + mockConsumer.updateBeginningOffsets(startOffsets); + + //ACT + + //service set up + BankAccountService bankAccountService = new BankAccountService(mockConsumer, hazelcastServiceTest); + Thread.sleep(10000); + //callbacks set up + bankAccountService.afterPropertiesSet(); + Thread.sleep(10000); + + //ASSERT + IMap iMap = hazelcastServiceTest.getHazelcast().getMap(IMDGDistributedNames.Map_BankAccount); + BankAccount result = iMap.get(currentId); + + BANK_ACCOUNT_MATCHER.assertMatch(result, predictableResult); + + //reset hazelcastService for next test + iMap.clear(); + currentId++; + } + + /** + * {@link BankAccountService#bankAccountUpdate(BaseRequest)}
+ * Тест проверяет обновление сущности {@link BankAccount} в Hazelcast при передаче из Apache Kafka.
+ * Входной запрос {@link BankAccountUpdateRequest}:
+ * {@link BankAccountUpdateRequest#bankIdentificationCode} - NEW BUNK NAME
+ * {@link BankAccountUpdateRequest#bankName} - 88888
+ * {@link BankAccountUpdateRequest#correspondentAccount} - 894984646541316
+ * {@link BankAccountUpdateRequest#correspondentAccountName} - BIK OF NEW BUNK
+ * {@link BankAccountUpdateRequest#currency} - EU
+ * {@link BankAccountUpdateRequest#destination} - OOO NEW BUNK
+ * {@link BankAccountUpdateRequest#taxpayerIdentificationNumber} - 65468461321
+ * {@link BankAccountUpdateRequest#taxRegistrationReasonCode} - 532137
+ * {@link BankAccountUpdateRequest#account} - 326984656514
+ */ + @Test + void bankAccountUpdate() throws InterruptedException { + //arrange + BankAccountNewRequest bankAccountNewRequest = new BankAccountNewRequest(); + bankAccountNewRequest.setBankName("ooo tinkoff"); + bankAccountNewRequest.setBankIdentificationCode("99999"); + bankAccountNewRequest.setCorrespondentAccount("9294189285498598598"); + bankAccountNewRequest.setCorrespondentAccountName("BIK OF TINKOFF"); + bankAccountNewRequest.setCurrency("RUB"); + bankAccountNewRequest.setDestination("OOO ROGA I KOPITA"); + bankAccountNewRequest.setTaxpayerIdentificationNumber("848484848484"); + bankAccountNewRequest.setTaxRegistrationReasonCode("886886"); + bankAccountNewRequest.setAccount("123456789123"); + + BaseRequest baseNewRequest = new BaseRequest<>(); + baseNewRequest.setRequestPayload(bankAccountNewRequest); + baseNewRequest.setId(currentId); + baseNewRequest.setActionType(ActionType.NEW); + String jsonBaseNewRequest; + ObjectMapper objectMapper = new ObjectMapper(); + try { + jsonBaseNewRequest = objectMapper.writeValueAsString(baseNewRequest); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + BankAccountUpdateRequest bankAccountUpdateRequest = new BankAccountUpdateRequest(); + bankAccountUpdateRequest.setId(currentId); + bankAccountUpdateRequest.setBankName("NEW BUNK NAME"); + bankAccountUpdateRequest.setBankIdentificationCode("88888"); + bankAccountUpdateRequest.setCorrespondentAccount("894984646541316"); + bankAccountUpdateRequest.setCorrespondentAccountName("BIK OF NEW BUNK"); + bankAccountUpdateRequest.setCurrency("EU"); + bankAccountUpdateRequest.setDestination("OOO NEW BUNK"); + bankAccountUpdateRequest.setTaxpayerIdentificationNumber("65468461321"); + bankAccountUpdateRequest.setTaxRegistrationReasonCode("532137"); + bankAccountUpdateRequest.setAccount("326984656514"); + + BaseRequest baseUpdateRequest = new BaseRequest<>(); + baseUpdateRequest.setRequestPayload(bankAccountUpdateRequest); + baseUpdateRequest.setId(currentId); + baseUpdateRequest.setActionType(ActionType.UPDATE); + String jsonBaseUpdateRequest; + try { + jsonBaseUpdateRequest = objectMapper.writeValueAsString(baseUpdateRequest); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + BankAccount predictableUpdateResult = new BankAccount(); + predictableUpdateResult.setBankName("NEW BUNK NAME"); + predictableUpdateResult.setBankIdentificationCode("88888"); + predictableUpdateResult.setCorrespondentAccount("894984646541316"); + predictableUpdateResult.setCorrespondentAccountName("BIK OF NEW BUNK"); + predictableUpdateResult.setCurrency("EU"); + predictableUpdateResult.setDestination("OOO NEW BUNK"); + predictableUpdateResult.setTaxpayerIdentificationNumber("65468461321"); + predictableUpdateResult.setTaxRegistrationReasonCode("532137"); + predictableUpdateResult.setAccount("326984656514"); + predictableUpdateResult.setId(currentId); + + //KAFKA + mockConsumer.schedulePollTask(() -> { + mockConsumer.rebalance(Collections.singletonList(new TopicPartition(TOPIC_ACCOUNT_NEW, PARTITION))); + mockConsumer.addRecord(new ConsumerRecord<>(TOPIC_ACCOUNT_NEW, PARTITION, 0, "key", jsonBaseNewRequest)); + }); + HashMap startOffsets = new HashMap<>(); + TopicPartition tp = new TopicPartition(TOPIC_ACCOUNT_NEW, PARTITION); + startOffsets.put(tp, 0L); + mockConsumer.updateBeginningOffsets(startOffsets); + + //ACT + //service set up + BankAccountService bankAccountService = new BankAccountService(mockConsumer, hazelcastServiceTest); + Thread.sleep(10000); + //callbacks set up + bankAccountService.afterPropertiesSet(); + Thread.sleep(10000); + + //KAFKA + mockConsumer.schedulePollTask(() -> { + mockConsumer.rebalance(Collections.singletonList(new TopicPartition(TOPIC_ACCOUNT_UPDATE, PARTITION))); + mockConsumer.addRecord(new ConsumerRecord<>(TOPIC_ACCOUNT_UPDATE, PARTITION, 0, "key", jsonBaseUpdateRequest)); + }); + HashMap startOffsetsUpdating = new HashMap<>(); + TopicPartition tpUpdating = new TopicPartition(TOPIC_ACCOUNT_UPDATE, PARTITION); + startOffsetsUpdating.put(tpUpdating, 0L); + mockConsumer.updateBeginningOffsets(startOffsetsUpdating); + + //ASSERT + Thread.sleep(10000); + IMap iMap = hazelcastServiceTest.getHazelcast().getMap(IMDGDistributedNames.Map_BankAccount); + BankAccount resultUpdating = iMap.get(currentId); + + BANK_ACCOUNT_MATCHER.assertMatch(resultUpdating, predictableUpdateResult); + + //reset hazelcastService for next test + iMap.clear(); + currentId++; + } + + /** + * {@link BankAccountService#bankAccountDelete(BaseRequest)} + * Тест проверяет удаление сущности {@link BankAccount} в Hazelcast при передаче из Apache Kafka. + * Входной запрос {@link CommonDeleteRequest}: + * {@link CommonDeleteRequest#id} - Идентификатор записи + */ + @Test + void bankAccountDelete() throws InterruptedException { + //arrange + BankAccountNewRequest bankAccountNewRequest = new BankAccountNewRequest(); + bankAccountNewRequest.setBankName("ooo tinkoff"); + bankAccountNewRequest.setBankIdentificationCode("99999"); + bankAccountNewRequest.setCorrespondentAccount("9294189285498598598"); + bankAccountNewRequest.setCorrespondentAccountName("BIK OF TINKOFF"); + bankAccountNewRequest.setCurrency("RUB"); + bankAccountNewRequest.setDestination("OOO ROGA I KOPITA"); + bankAccountNewRequest.setTaxpayerIdentificationNumber("848484848484"); + bankAccountNewRequest.setTaxRegistrationReasonCode("886886"); + bankAccountNewRequest.setAccount("123456789123"); + + BaseRequest baseNewRequest = new BaseRequest<>(); + baseNewRequest.setRequestPayload(bankAccountNewRequest); + baseNewRequest.setId(currentId); + baseNewRequest.setActionType(ActionType.NEW); + String jsonBaseNewRequest; + ObjectMapper objectMapper = new ObjectMapper(); + try { + jsonBaseNewRequest = objectMapper.writeValueAsString(baseNewRequest); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + + CommonDeleteRequest commonDeleteRequest = new CommonDeleteRequest(); + commonDeleteRequest.setId(currentId); + BaseRequest baseDeleteRequest = new BaseRequest<>(); + baseDeleteRequest.setRequestPayload(commonDeleteRequest); + baseDeleteRequest.setId(currentId); + baseDeleteRequest.setActionType(ActionType.DELETE); + String jsonDeleteNewRequest; + try { + jsonDeleteNewRequest = objectMapper.writeValueAsString(baseDeleteRequest); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + //KAFKA + mockConsumer.schedulePollTask(() -> { + mockConsumer.rebalance(Collections.singletonList(new TopicPartition(TOPIC_ACCOUNT_NEW, PARTITION))); + mockConsumer.addRecord(new ConsumerRecord<>(TOPIC_ACCOUNT_NEW, PARTITION, 0, "key", jsonBaseNewRequest)); + }); + HashMap startOffsets = new HashMap<>(); + TopicPartition tp = new TopicPartition(TOPIC_ACCOUNT_NEW, PARTITION); + startOffsets.put(tp, 0L); + mockConsumer.updateBeginningOffsets(startOffsets); + + //ACT + //service set up + BankAccountService bankAccountService = new BankAccountService(mockConsumer, hazelcastServiceTest); + Thread.sleep(10000); + //callbacks set up + bankAccountService.afterPropertiesSet(); + Thread.sleep(10000); + + //KAFKA + mockConsumer.schedulePollTask(() -> { + mockConsumer.rebalance(Collections.singletonList(new TopicPartition(TOPIC_ACCOUNT_DELETE, PARTITION))); + mockConsumer.addRecord(new ConsumerRecord<>(TOPIC_ACCOUNT_DELETE, PARTITION, 0, "key", jsonDeleteNewRequest)); + }); + HashMap startOffsetsDeleting = new HashMap<>(); + TopicPartition tpDeleting = new TopicPartition(TOPIC_ACCOUNT_DELETE, PARTITION); + startOffsetsDeleting.put(tpDeleting, 0L); + mockConsumer.updateBeginningOffsets(startOffsetsDeleting); + + //ASSERT + Thread.sleep(10000); + IMap iMap = hazelcastServiceTest.getHazelcast().getMap(IMDGDistributedNames.Map_BankAccount); + Assertions.assertEquals(0, iMap.size()); + + //reset hazelcastService for next test + iMap.clear(); + currentId++; + } +} \ No newline at end of file diff --git a/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/utils/MatcherFactory.java b/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/utils/MatcherFactory.java new file mode 100644 index 000000000..b2d1a4fdc --- /dev/null +++ b/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/utils/MatcherFactory.java @@ -0,0 +1,38 @@ +package ru.spcex.clearing.account.utils; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Factory for creating test matchers. + *

+ * Comparing actual and expected objects via AssertJ + */ +public class MatcherFactory { + + public static Matcher usingIgnoringFieldsComparator(String... fieldsToIgnore) { + return new Matcher<>(fieldsToIgnore); + } + + public static class Matcher { + private final String[] fieldsToIgnore; + + private Matcher(String... fieldsToIgnore) { + this.fieldsToIgnore = fieldsToIgnore; + } + + public void assertMatch(T actual, T expected) { + assertThat(actual).usingRecursiveComparison().ignoringFields(fieldsToIgnore).isEqualTo(expected); + } + + @SafeVarargs + public final void assertMatch(Iterable actual, T... expected) { + assertMatch(actual, Arrays.asList(expected)); + } + + public void assertMatch(Iterable actual, Iterable expected) { + assertThat(actual).usingRecursiveFieldByFieldElementComparatorIgnoringFields(fieldsToIgnore).isEqualTo(expected); + } + } +} diff --git a/clearing-parent/company-service/pom.xml b/clearing-parent/company-service/pom.xml index cd323c846..a34291ee8 100644 --- a/clearing-parent/company-service/pom.xml +++ b/clearing-parent/company-service/pom.xml @@ -1,6 +1,6 @@ - clearing-parent @@ -36,6 +36,11 @@ com.fasterxml.jackson.core jackson-databind + + org.springframework.boot + spring-boot-starter-test + test + @@ -63,6 +68,23 @@ ${project.artifactId} + + org.apache.maven.plugins + maven-surefire-plugin + 2.21.0 + + + org.junit.platform + junit-platform-surefire-provider + 1.2.0-M1 + + + org.junit.jupiter + junit-jupiter-engine + 5.2.0-M1 + + + diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/CompanySymbolService.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/CompanySymbolService.java index 152fac925..627c7b041 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/CompanySymbolService.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/CompanySymbolService.java @@ -41,7 +41,7 @@ public class CompanySymbolService extends QueueConsumer implements InitializingB CompanySymbolUpdateRequest req = userRequest.getRequestPayload(); log.debug("CompanySymbolUpdateRequest received"); CompanySymbols companySymbols = companySymbolsMap.getSingleObjectByID(req.getId()); - companySymbols.setCompanySymbol(req.getCompanySymbol()); +// companySymbols.setCompanySymbol(req.getCompanySymbol()); по ТЗ не должен менятся при обновлении companySymbols.setCompanySymbolValue(req.getCompanySymbolValue()); companySymbolsMap.update(companySymbols); diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/ContactService.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/ContactService.java index b38bf9ab2..44940401c 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/ContactService.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/ContactService.java @@ -6,6 +6,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; import ru.clearing.classes.statics.data.profile.Contact; import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.platform.messaging.domain.BaseRequest; @@ -15,6 +16,7 @@ import ru.spcex.clearing.platform.messaging.service.QueueConsumer; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgProvider; +@Service public class ContactService extends QueueConsumer implements InitializingBean { private final Logger log = LoggerFactory.getLogger(getClass()); private final Imdg contactMap; @@ -38,7 +40,7 @@ public class ContactService extends QueueConsumer implements InitializingBean { ContactUpdateRequest req = userRequest.getRequestPayload(); log.debug("ContactUpdateRequest received"); Contact contact = contactMap.getSingleObjectByID(req.getId()); - contact.setContactType(req.getContactType()); +// contact.setContactType(req.getContactType()); по ТЗ не должен менятся при обновлении contact.setContactValue(req.getContactValue()); contactMap.update(contact); diff --git a/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/config/HazelcastServiceTestConfiguration.java b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/config/HazelcastServiceTestConfiguration.java new file mode 100644 index 000000000..0c7ca4069 --- /dev/null +++ b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/config/HazelcastServiceTestConfiguration.java @@ -0,0 +1,68 @@ +package ru.spcex.clearing.company.config; + +import com.hazelcast.config.*; +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import ru.spcex.platform.imdg.iml.hazelcast.config.HazelcastClientParams; +import ru.spcex.platform.imdg.iml.hazelcast.service.HazelcastService; +import ru.spcex.platform.imdg.iml.hazelcast.util.HazelcastHelper; + +import java.util.List; +import java.util.Random; + +@Configuration +public class HazelcastServiceTestConfiguration { + private HazelcastInstance hazelcastInstance; + + private static ThreadPoolTaskExecutor createThreadPoolTaskExecutor(int maxPoolSz, boolean waitForCompletion) { + ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor(); + if (maxPoolSz > 2) { + pool.setKeepAliveSeconds(60); + pool.setAllowCoreThreadTimeOut(true); + } + pool.setCorePoolSize(maxPoolSz); + pool.setWaitForTasksToCompleteOnShutdown(waitForCompletion); + return pool; + } + + @Bean(name = "hazelcastServiceTest") + public HazelcastService hazelcastService(@Qualifier("taskExecutorHazelcastClientInitializer") ThreadPoolTaskExecutor taskExecutorHazelcastClientInitializer, @Qualifier("taskExecutorIdGeneratorAwaiter") ThreadPoolTaskExecutor taskExecutorIdGeneratorAwaiter, HazelcastClientParams params) { + Config cfg = new Config(); + cfg.setInstanceName("localhost"); + + NetworkConfig networkConfig = new NetworkConfig(); + JoinConfig joinConfig = new JoinConfig(); + joinConfig.setMulticastConfig(new MulticastConfig().setEnabled(false)); + joinConfig.setTcpIpConfig(new TcpIpConfig().setEnabled(true).setMembers(List.of("127.0.0.1"))); + networkConfig.setJoin(joinConfig); + cfg.setNetworkConfig(networkConfig); + hazelcastInstance = Hazelcast.newHazelcastInstance(cfg); + HazelcastHelper.otcSystem_setStorageState(true, hazelcastInstance); + return new HazelcastService(taskExecutorHazelcastClientInitializer, taskExecutorIdGeneratorAwaiter, params); + } + + @Bean(name = "taskExecutorHazelcastClientInitializer") + public ThreadPoolTaskExecutor taskExecutorHazelcastClientInitializer() { + return createThreadPoolTaskExecutor(1, true); + } + + @Bean(name = "taskExecutorIdGeneratorAwaiter") + public ThreadPoolTaskExecutor taskExecutorIdGeneratorAwaiter() { + return createThreadPoolTaskExecutor(1, false); + } + + @Bean(name = "hazelcastClientParams") + public HazelcastClientParams getHazelcastClientParams() { + HazelcastClientParams params = new HazelcastClientParams(); + params.setLogin("dev"); + params.setPassword("dev-pass"); + params.setClusterMembers("127.0.0.1"); + params.setInstanceName("hzTestClient" + new Random().nextInt()); + params.setNearCacheConfig(new NearCacheConfig()); + return params; + } +} diff --git a/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/ClearingMemberCategoryServiceTest.java b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/ClearingMemberCategoryServiceTest.java new file mode 100644 index 000000000..fed259cb8 --- /dev/null +++ b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/ClearingMemberCategoryServiceTest.java @@ -0,0 +1,211 @@ +package ru.spcex.clearing.company.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.hazelcast.core.IMap; +import com.hazelcast.map.listener.EntryRemovedListener; +import com.hazelcast.map.listener.EntryUpdatedListener; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.MockConsumer; +import org.apache.kafka.clients.consumer.OffsetResetStrategy; +import org.apache.kafka.common.TopicPartition; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import ru.clearing.classes.statics.data.generated.ClearingMemberCategory; +import ru.clearing.classes.statics.data.profile.Contact; +import ru.spcex.clearing.company.config.HazelcastServiceTestConfiguration; +import ru.spcex.clearing.company.utils.MatcherFactory.Matcher; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.ActionType; +import ru.spcex.clearing.platform.messaging.domain.BaseRequest; +import ru.spcex.clearing.platform.messaging.domain.Consts; +import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonDeleteRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.company.ClearingMemberCategoryUpdateRequest; +import ru.spcex.platform.imdg.iml.hazelcast.service.HazelcastService; + +import java.util.Collections; +import java.util.HashMap; + +import static ru.spcex.clearing.company.utils.MatcherFactory.usingIgnoringFieldsComparator; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + HazelcastServiceTestConfiguration.class}) +class ClearingMemberCategoryServiceTest { + + public static final Matcher MEMBER_CATEGORY_MATCHER = usingIgnoringFieldsComparator(); + private static final int PARTITION = 0; + private static final String TOPIC_MEMBER_CATEGORY_UPDATE = Consts.DESTINATION_CLEARING_MEMBER_CATEGORY_UPDATE; + private static final String TOPIC_MEMBER_CATEGORY_DELETE = Consts.DESTINATION_CLEARING_MEMBER_CATEGORY_DELETE; + private static final Long ID = 0L; + + @Autowired + @Qualifier("hazelcastServiceTest") + private HazelcastService hazelcastServiceTest; + private MockConsumer mockConsumer; + + @BeforeEach + void setUp() { + mockConsumer = new MockConsumer<>(OffsetResetStrategy.EARLIEST); + } + + /** + * {@link ClearingMemberCategoryService#clearingMemberCategoryUpdate(BaseRequest)}
+ * Тест проверяет обновление сущности {@link ClearingMemberCategory} в Hazelcast при передаче из Apache Kafka.
+ * Входной запрос {@link ClearingMemberCategoryUpdateRequest}:
+ * {@link ClearingMemberCategoryUpdateRequest#id} - Идентификатор записи
+ * {@link ClearingMemberCategoryUpdateRequest#clearingMemberCategory} - 1234
+ */ + @Test + void clearingMemberCategoryUpdate() throws InterruptedException { + //ARRANGE + ClearingMemberCategory existsСlearingMemberCategory = new ClearingMemberCategory(); + existsСlearingMemberCategory.setId(ID); + existsСlearingMemberCategory.setClearingMemberCategory("0000"); + + ClearingMemberCategoryUpdateRequest memberCategoryUpdateRequest = new ClearingMemberCategoryUpdateRequest(); + memberCategoryUpdateRequest.setId(ID); + memberCategoryUpdateRequest.setClearingMemberCategory("1234"); + BaseRequest baseUpdateRequest = new BaseRequest<>(); + baseUpdateRequest.setRequestPayload(memberCategoryUpdateRequest); + baseUpdateRequest.setId(ID); + baseUpdateRequest.setActionType(ActionType.UPDATE); + String jsonBaseForUpdatingRequest; + ObjectMapper objectMapper = new ObjectMapper(); + try { + jsonBaseForUpdatingRequest = objectMapper.writeValueAsString(baseUpdateRequest); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + + ClearingMemberCategory predictableClearingMemberCategory = new ClearingMemberCategory(); + predictableClearingMemberCategory.setId(ID); + predictableClearingMemberCategory.setClearingMemberCategory("1234"); + + //ACT + //service set up + ClearingMemberCategoryService clearingMemberCategoryService = new ClearingMemberCategoryService(mockConsumer, hazelcastServiceTest); + + //callbacks set up + clearingMemberCategoryService.afterPropertiesSet(); + + IMap iMap = hazelcastServiceTest.getHazelcast().getMap(IMDGDistributedNames.Map_ClearingMemberCategory); + iMap.put(ID, existsСlearingMemberCategory); + + //KAFKA + mockConsumer.schedulePollTask(() -> { + mockConsumer.rebalance(Collections.singletonList(new TopicPartition(TOPIC_MEMBER_CATEGORY_UPDATE, PARTITION))); + mockConsumer.addRecord(new ConsumerRecord<>(TOPIC_MEMBER_CATEGORY_UPDATE, PARTITION, 0, "key", jsonBaseForUpdatingRequest)); + }); + HashMap startOffsetsUpdating = new HashMap<>(); + TopicPartition tpUpdating = new TopicPartition(TOPIC_MEMBER_CATEGORY_UPDATE, PARTITION); + startOffsetsUpdating.put(tpUpdating, 0L); + mockConsumer.updateBeginningOffsets(startOffsetsUpdating); + + //waiting for hazelcast map item updates + Object waiter = new Object(); + String listenerID = iMap.addEntryListener((EntryUpdatedListener) entryEvent -> { + System.out.println("Checking If removed.."); + + synchronized (waiter) { + try { + waiter.wait(100); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + waiter.notify(); + } + }, false); + + synchronized (waiter) { + waiter.wait(100); + } + + //ASSERT + ClearingMemberCategory resultUpdating = iMap.get(ID); + MEMBER_CATEGORY_MATCHER.assertMatch(resultUpdating, predictableClearingMemberCategory); + + //preparing hazelcastImdgProvider for next test + iMap.removeEntryListener(listenerID); + } + + /** + * {@link ClearingMemberCategoryService#clearingMemberCategoryDelete(BaseRequest)}
+ * Тест проверяет удаление сущности {@link ClearingMemberCategory} в Hazelcast при передаче из Apache Kafka.
+ * Входной запрос {@link CommonDeleteRequest}:
+ * {@link CommonDeleteRequest#id} - Идентификатор записи
+ */ + @Test + void clearingMemberCategoryDelete() throws InterruptedException { + //ARRANGE + ClearingMemberCategory existsСlearingMemberCategory = new ClearingMemberCategory(); + existsСlearingMemberCategory.setId(ID); + existsСlearingMemberCategory.setClearingMemberCategory("0000"); + + CommonDeleteRequest memberCategoryDeleteRequest = new CommonDeleteRequest(); + memberCategoryDeleteRequest.setId(ID); + + BaseRequest baseDeleteRequest = new BaseRequest<>(); + baseDeleteRequest.setRequestPayload(memberCategoryDeleteRequest); + baseDeleteRequest.setId(ID); + baseDeleteRequest.setActionType(ActionType.DELETE); + String jsonBaseForDeleteRequest; + ObjectMapper objectMapper = new ObjectMapper(); + try { + jsonBaseForDeleteRequest = objectMapper.writeValueAsString(baseDeleteRequest); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + + //ACT + //service set up + ClearingMemberCategoryService clearingMemberCategoryService = new ClearingMemberCategoryService(mockConsumer, hazelcastServiceTest); + + //callbacks set up + clearingMemberCategoryService.afterPropertiesSet(); + + IMap iMap = hazelcastServiceTest.getHazelcast().getMap(IMDGDistributedNames.Map_ClearingMemberCategory); + iMap.put(ID, existsСlearingMemberCategory); + + //KAFKA + mockConsumer.schedulePollTask(() -> { + mockConsumer.rebalance(Collections.singletonList(new TopicPartition(TOPIC_MEMBER_CATEGORY_DELETE, PARTITION))); + mockConsumer.addRecord(new ConsumerRecord<>(TOPIC_MEMBER_CATEGORY_DELETE, PARTITION, 0, "key", jsonBaseForDeleteRequest)); + }); + HashMap startOffsetsUpdating = new HashMap<>(); + TopicPartition tpDeleting = new TopicPartition(TOPIC_MEMBER_CATEGORY_DELETE, PARTITION); + startOffsetsUpdating.put(tpDeleting, 0L); + mockConsumer.updateBeginningOffsets(startOffsetsUpdating); + + //waiting for hazelcast map item removes + Object waiter = new Object(); + String listenerID = iMap.addEntryListener((EntryRemovedListener) entryEvent -> { + System.out.println("Checking If removed.."); + + try { + waiter.wait(100); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + synchronized (waiter) { + waiter.notify(); + } + }, false); + + synchronized (waiter) { + waiter.wait(100); + } + + //ASSERT + Assertions.assertEquals(0, iMap.size()); + + //preparing hazelcastImdgProvider for next test + iMap.removeEntryListener(listenerID); + } +} \ No newline at end of file diff --git a/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/CompanyInfoServiceTest.java b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/CompanyInfoServiceTest.java new file mode 100644 index 000000000..e88dedc01 --- /dev/null +++ b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/CompanyInfoServiceTest.java @@ -0,0 +1,176 @@ +package ru.spcex.clearing.company.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.hazelcast.core.IMap; +import com.hazelcast.map.listener.EntryUpdatedListener; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.MockConsumer; +import org.apache.kafka.clients.consumer.OffsetResetStrategy; +import org.apache.kafka.common.TopicPartition; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import ru.clearing.classes.statics.data.profile.CompanyInfo; +import ru.clearing.classes.statics.data.profile.Contact; +import ru.spcex.clearing.company.config.HazelcastServiceTestConfiguration; +import ru.spcex.clearing.company.utils.MatcherFactory; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.ActionType; +import ru.spcex.clearing.platform.messaging.domain.BaseRequest; +import ru.spcex.clearing.platform.messaging.domain.Consts; +import ru.spcex.clearing.platform.messaging.domain.cud.company.CompanyInfoUpdateRequest; +import ru.spcex.platform.imdg.iml.hazelcast.service.HazelcastService; + +import java.util.Collections; +import java.util.HashMap; + +import static ru.spcex.clearing.company.utils.MatcherFactory.usingIgnoringFieldsComparator; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + HazelcastServiceTestConfiguration.class}) +class CompanyInfoServiceTest { + + public static final MatcherFactory.Matcher COMPANY_INFO_MATCHER = usingIgnoringFieldsComparator(); + private static final int PARTITION = 0; + private static final String TOPIC_COMPANY_INFO_UPDATE = Consts.DESTINATION_COMPANY_INFO_UPDATE; + private static final Long ID = 0L; + + @Autowired + @Qualifier("hazelcastServiceTest") + private HazelcastService hazelcastServiceTest; + private MockConsumer mockConsumer; + + @BeforeEach + void setUp() { + mockConsumer = new MockConsumer<>(OffsetResetStrategy.EARLIEST); + } + + /** + * {@link CompanyInfoService#companyInfoUpdate(BaseRequest)}
+ * Тест проверяет обновление сущности {@link CompanyInfo} в Hazelcast при передаче из Apache Kafka.
+ * Входной запрос {@link CompanyInfoUpdateRequest}:
+ * {@link CompanyInfoUpdateRequest#id} - Идентификатор записи
+ * {@link CompanyInfoUpdateRequest#corporationSoleType} - 1234
+ * {@link CompanyInfoUpdateRequest#countryCode} - 1234
+ * {@link CompanyInfoUpdateRequest#description} - updated description
+ * {@link CompanyInfoUpdateRequest#professionalSign} - 1234
+ * {@link CompanyInfoUpdateRequest#legalKind} - 1234
+ * {@link CompanyInfoUpdateRequest#organizationType} - 1234
+ * {@link CompanyInfoUpdateRequest#residence} - 1234
+ * {@link CompanyInfoUpdateRequest#shortNameEng} - updated shortNameEng
+ * {@link CompanyInfoUpdateRequest#fullNameEng} - updated fullNameEng
+ * {@link CompanyInfoUpdateRequest#shortName} - updated shortName
+ * {@link CompanyInfoUpdateRequest#fullName} - updated fullName
+ */ + @Test + void companyInfoUpdate() throws InterruptedException { + //ARRANGE + CompanyInfo existsCompanyInfo = new CompanyInfo(); + existsCompanyInfo.setId(ID); + existsCompanyInfo.setCompanyId(ID); + existsCompanyInfo.setCorporationSoleType("0000"); + existsCompanyInfo.setCountryCode("0000"); + existsCompanyInfo.setDescription("exists description"); + existsCompanyInfo.setProfessionalSign("0000"); + existsCompanyInfo.setLegalKind("0000"); + existsCompanyInfo.setOrganizationType("0000"); + existsCompanyInfo.setResidence("0000"); + existsCompanyInfo.setShortNameEng("exists shortNameEng"); + existsCompanyInfo.setFullNameEng("exists fullNameEng"); + existsCompanyInfo.setShortName("exists shortName"); + existsCompanyInfo.setFullName("exists fullName"); + + CompanyInfoUpdateRequest companyInfoUpdateRequest = new CompanyInfoUpdateRequest(); + companyInfoUpdateRequest.setId(ID); + companyInfoUpdateRequest.setCorporationSoleType("1234"); + companyInfoUpdateRequest.setCountryCode("1234"); + companyInfoUpdateRequest.setDescription("updated description"); + companyInfoUpdateRequest.setProfessionalSign("1234"); + companyInfoUpdateRequest.setLegalKind("1234"); + companyInfoUpdateRequest.setOrganizationType("1234"); + companyInfoUpdateRequest.setResidence("1234"); + companyInfoUpdateRequest.setShortNameEng("updated shortNameEng"); + companyInfoUpdateRequest.setFullNameEng("updated fullNameEng"); + companyInfoUpdateRequest.setShortName("updated shortName"); + companyInfoUpdateRequest.setFullName("updated fullName"); + + BaseRequest baseUpdateRequest = new BaseRequest<>(); + baseUpdateRequest.setRequestPayload(companyInfoUpdateRequest); + baseUpdateRequest.setId(ID); + baseUpdateRequest.setActionType(ActionType.UPDATE); + String jsonBaseForUpdatingRequest; + ObjectMapper objectMapper = new ObjectMapper(); + try { + jsonBaseForUpdatingRequest = objectMapper.writeValueAsString(baseUpdateRequest); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + + CompanyInfo predictableCompanyInfo = new CompanyInfo(); + predictableCompanyInfo.setId(ID); + predictableCompanyInfo.setCompanyId(ID); + predictableCompanyInfo.setCorporationSoleType("1234"); + predictableCompanyInfo.setCountryCode("1234"); + predictableCompanyInfo.setDescription("updated description"); + predictableCompanyInfo.setProfessionalSign("1234"); + predictableCompanyInfo.setLegalKind("1234"); + predictableCompanyInfo.setOrganizationType("1234"); + predictableCompanyInfo.setResidence("1234"); + predictableCompanyInfo.setShortNameEng("updated shortNameEng"); + predictableCompanyInfo.setFullNameEng("updated fullNameEng"); + predictableCompanyInfo.setShortName("updated shortName"); + predictableCompanyInfo.setFullName("updated fullName"); + + //ACT + //service set up + CompanyInfoService clearingMemberCategoryService = new CompanyInfoService(mockConsumer, hazelcastServiceTest); + + //callbacks set up + clearingMemberCategoryService.afterPropertiesSet(); + + IMap iMap = hazelcastServiceTest.getHazelcast().getMap(IMDGDistributedNames.Map_Company); + iMap.put(ID, existsCompanyInfo); + + //KAFKA + mockConsumer.schedulePollTask(() -> { + mockConsumer.rebalance(Collections.singletonList(new TopicPartition(TOPIC_COMPANY_INFO_UPDATE, PARTITION))); + mockConsumer.addRecord(new ConsumerRecord<>(TOPIC_COMPANY_INFO_UPDATE, PARTITION, 0, "key", jsonBaseForUpdatingRequest)); + }); + HashMap startOffsetsUpdating = new HashMap<>(); + TopicPartition tpUpdating = new TopicPartition(TOPIC_COMPANY_INFO_UPDATE, PARTITION); + startOffsetsUpdating.put(tpUpdating, 0L); + mockConsumer.updateBeginningOffsets(startOffsetsUpdating); + + //waiting for hazelcast map updates + Object waiter = new Object(); + String listenerID = iMap.addEntryListener((EntryUpdatedListener) entryEvent -> { + System.out.println("Checking If removed.."); + + try { + waiter.wait(100); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + synchronized (waiter) { + waiter.notify(); + } + }, false); + + synchronized (waiter) { + waiter.wait(100); + } + + //ASSERT + CompanyInfo resultUpdating = iMap.get(ID); + COMPANY_INFO_MATCHER.assertMatch(resultUpdating, predictableCompanyInfo); + + //preparing hazelcastImdgProvider for next test + iMap.removeEntryListener(listenerID); + } +} \ No newline at end of file diff --git a/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/CompanyServiceTest.java b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/CompanyServiceTest.java new file mode 100644 index 000000000..b0c93177e --- /dev/null +++ b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/CompanyServiceTest.java @@ -0,0 +1,128 @@ +package ru.spcex.clearing.company.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.hazelcast.core.IMap; +import com.hazelcast.map.listener.EntryRemovedListener; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.MockConsumer; +import org.apache.kafka.clients.consumer.OffsetResetStrategy; +import org.apache.kafka.common.TopicPartition; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import ru.clearing.classes.statics.data.company.Company; +import ru.clearing.classes.statics.data.generated.ClearingMemberCategory; +import ru.clearing.classes.statics.data.profile.Contact; +import ru.spcex.clearing.company.config.HazelcastServiceTestConfiguration; +import ru.spcex.clearing.company.utils.MatcherFactory; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.ActionType; +import ru.spcex.clearing.platform.messaging.domain.BaseRequest; +import ru.spcex.clearing.platform.messaging.domain.Consts; +import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonDeleteRequest; +import ru.spcex.platform.imdg.iml.hazelcast.service.HazelcastService; + +import java.util.Collections; +import java.util.HashMap; + +import static ru.spcex.clearing.company.utils.MatcherFactory.usingIgnoringFieldsComparator; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + HazelcastServiceTestConfiguration.class}) +class CompanyServiceTest { + + public static final MatcherFactory.Matcher COMPANY_MATCHER = usingIgnoringFieldsComparator(); + private static final int PARTITION = 0; + private static final String TOPIC_COMPANY_DELETE = Consts.DESTINATION_COMPANY_DELETE; + private static final Long ID = 0L; + + @Autowired + @Qualifier("hazelcastServiceTest") + private HazelcastService hazelcastServiceTest; + private MockConsumer mockConsumer; + + @BeforeEach + void setUp() { + mockConsumer = new MockConsumer<>(OffsetResetStrategy.EARLIEST); + } + + /** + * {@link CompanyService#deleteCompany(BaseRequest)}
+ * Тест проверяет удаление сущности {@link Company} в Hazelcast при передаче из Apache Kafka.
+ * Входной запрос {@link CommonDeleteRequest}:
+ * {@link CommonDeleteRequest#id} - Идентификатор записи
+ */ + @Test + void deleteCompany() throws InterruptedException { + //ARRANGE + Company existsCompany = new Company(); + existsCompany.setId(ID); + + CommonDeleteRequest memberCategoryDeleteRequest = new CommonDeleteRequest(); + memberCategoryDeleteRequest.setId(ID); + + BaseRequest baseDeleteRequest = new BaseRequest<>(); + baseDeleteRequest.setRequestPayload(memberCategoryDeleteRequest); + baseDeleteRequest.setId(ID); + baseDeleteRequest.setActionType(ActionType.DELETE); + String jsonBaseForDeleteRequest; + ObjectMapper objectMapper = new ObjectMapper(); + try { + jsonBaseForDeleteRequest = objectMapper.writeValueAsString(baseDeleteRequest); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + + //ACT + //service set up + CompanyService companyService = new CompanyService(mockConsumer, hazelcastServiceTest); + + //callbacks set up + companyService.afterPropertiesSet(); + + IMap iMap = hazelcastServiceTest.getHazelcast().getMap(IMDGDistributedNames.Map_Company); + iMap.put(ID, existsCompany); + + //KAFKA + mockConsumer.schedulePollTask(() -> { + mockConsumer.rebalance(Collections.singletonList(new TopicPartition(TOPIC_COMPANY_DELETE, PARTITION))); + mockConsumer.addRecord(new ConsumerRecord<>(TOPIC_COMPANY_DELETE, PARTITION, 0, "key", jsonBaseForDeleteRequest)); + }); + HashMap startOffsetsUpdating = new HashMap<>(); + TopicPartition tpDeleting = new TopicPartition(TOPIC_COMPANY_DELETE, PARTITION); + startOffsetsUpdating.put(tpDeleting, 0L); + mockConsumer.updateBeginningOffsets(startOffsetsUpdating); + + //waiting for hazelcast map item removes + Object waiter = new Object(); + String listenerID = iMap.addEntryListener((EntryRemovedListener) entryEvent -> { + System.out.println("Checking If removed.."); + + try { + waiter.wait(100); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + synchronized (waiter) { + waiter.notify(); + } + }, false); + + synchronized (waiter) { + waiter.wait(100); + } + + //ASSERT + Assertions.assertEquals(0, iMap.size()); + + //preparing hazelcastImdgProvider for next test + iMap.removeEntryListener(listenerID); + } +} \ No newline at end of file diff --git a/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/CompanySymbolServiceTest.java b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/CompanySymbolServiceTest.java new file mode 100644 index 000000000..b070b41f2 --- /dev/null +++ b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/CompanySymbolServiceTest.java @@ -0,0 +1,142 @@ +package ru.spcex.clearing.company.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.hazelcast.core.IMap; +import com.hazelcast.map.listener.EntryUpdatedListener; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.MockConsumer; +import org.apache.kafka.clients.consumer.OffsetResetStrategy; +import org.apache.kafka.common.TopicPartition; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import ru.clearing.classes.statics.data.company.CompanySymbols; +import ru.clearing.classes.statics.data.profile.Contact; +import ru.spcex.clearing.company.config.HazelcastServiceTestConfiguration; +import ru.spcex.clearing.company.utils.MatcherFactory; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.ActionType; +import ru.spcex.clearing.platform.messaging.domain.BaseRequest; +import ru.spcex.clearing.platform.messaging.domain.Consts; +import ru.spcex.clearing.platform.messaging.domain.cud.company.CompanySymbolUpdateRequest; +import ru.spcex.platform.imdg.iml.hazelcast.service.HazelcastService; + +import java.util.Collections; +import java.util.HashMap; + +import static ru.spcex.clearing.company.utils.MatcherFactory.usingIgnoringFieldsComparator; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + HazelcastServiceTestConfiguration.class}) +class CompanySymbolServiceTest { + + public static final MatcherFactory.Matcher COMPANY_SYMBOL_MATCHER = usingIgnoringFieldsComparator(); + private static final int PARTITION = 0; + private static final String TOPIC_COMPANY_SYMBOL_UPDATE = Consts.DESTINATION_COMPANY_SYMBOL_UPDATE; + private static final Long ID = 0L; + + @Autowired + @Qualifier("hazelcastServiceTest") + private HazelcastService hazelcastServiceTest; + private MockConsumer mockConsumer; + + @BeforeEach + void setUp() { + mockConsumer = new MockConsumer<>(OffsetResetStrategy.EARLIEST); + } + + /** + * {@link CompanySymbolService#companySymbolUpdate(BaseRequest)}
+ * Тест проверяет обновление сущности {@link CompanySymbols} в Hazelcast при передаче из Apache Kafka.
+ * Входной запрос {@link CompanySymbolUpdateRequest}:
+ * {@link CompanySymbolUpdateRequest#id} - Идентификатор записи
+ * {@link CompanySymbolUpdateRequest#companyId} - Идентификатор записи
+ * {@link CompanySymbolUpdateRequest#companySymbol} - 1234
+ * {@link CompanySymbolUpdateRequest#companySymbolValue} - new companySymbolValue
+ */ + @Test + void companySymbolUpdate() throws InterruptedException { + //ARRANGE + CompanySymbols existsCompanySymbols = new CompanySymbols(); + existsCompanySymbols.setId(ID); + existsCompanySymbols.setCompanyId(ID); + existsCompanySymbols.setCompanySymbol("0000"); + existsCompanySymbols.setCompanySymbolValue("exists companySymbolValue"); + + CompanySymbolUpdateRequest companySymbolUpdateRequest = new CompanySymbolUpdateRequest(); + companySymbolUpdateRequest.setId(ID); + companySymbolUpdateRequest.setCompanyId(ID); + companySymbolUpdateRequest.setCompanySymbol("1234"); + companySymbolUpdateRequest.setCompanySymbolValue("new companySymbolValue"); + + BaseRequest baseUpdateRequest = new BaseRequest<>(); + baseUpdateRequest.setRequestPayload(companySymbolUpdateRequest); + baseUpdateRequest.setId(ID); + baseUpdateRequest.setActionType(ActionType.UPDATE); + String jsonBaseForUpdatingRequest; + ObjectMapper objectMapper = new ObjectMapper(); + try { + jsonBaseForUpdatingRequest = objectMapper.writeValueAsString(baseUpdateRequest); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + + CompanySymbols predictableCompanySymbols = new CompanySymbols(); + predictableCompanySymbols.setId(ID); + predictableCompanySymbols.setCompanyId(ID); + predictableCompanySymbols.setCompanySymbol("0000"); + predictableCompanySymbols.setCompanySymbolValue("new companySymbolValue"); + + //ACT + //service set up + CompanySymbolService companySymbolService = new CompanySymbolService(mockConsumer, hazelcastServiceTest); + + //callbacks set up + companySymbolService.afterPropertiesSet(); + + IMap iMap = hazelcastServiceTest.getHazelcast().getMap(IMDGDistributedNames.Map_CompanySymbols); + iMap.put(ID, existsCompanySymbols); + + //KAFKA + mockConsumer.schedulePollTask(() -> { + mockConsumer.rebalance(Collections.singletonList(new TopicPartition(TOPIC_COMPANY_SYMBOL_UPDATE, PARTITION))); + mockConsumer.addRecord(new ConsumerRecord<>(TOPIC_COMPANY_SYMBOL_UPDATE, PARTITION, 0, "key", jsonBaseForUpdatingRequest)); + }); + HashMap startOffsetsUpdating = new HashMap<>(); + TopicPartition tpUpdating = new TopicPartition(TOPIC_COMPANY_SYMBOL_UPDATE, PARTITION); + startOffsetsUpdating.put(tpUpdating, 0L); + mockConsumer.updateBeginningOffsets(startOffsetsUpdating); + + //waiting for hazelcast map item updates + Object waiter = new Object(); + String listenerID = iMap.addEntryListener((EntryUpdatedListener) entryEvent -> { + System.out.println("Checking If removed.."); + + try { + waiter.wait(100); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + synchronized (waiter) { + waiter.notify(); + } + }, false); + + synchronized (waiter) { + waiter.wait(100); + } + + //ASSERT + CompanySymbols resultUpdating = iMap.get(ID); + COMPANY_SYMBOL_MATCHER.assertMatch(resultUpdating, predictableCompanySymbols); + + //preparing hazelcastImdgProvider for next test + iMap.removeEntryListener(listenerID); + } +} \ No newline at end of file diff --git a/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/ContactServiceTest.java b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/ContactServiceTest.java new file mode 100644 index 000000000..55ed50790 --- /dev/null +++ b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/ContactServiceTest.java @@ -0,0 +1,140 @@ +package ru.spcex.clearing.company.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.hazelcast.core.IMap; +import com.hazelcast.map.listener.EntryUpdatedListener; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.MockConsumer; +import org.apache.kafka.clients.consumer.OffsetResetStrategy; +import org.apache.kafka.common.TopicPartition; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import ru.clearing.classes.statics.data.profile.Contact; +import ru.spcex.clearing.company.config.HazelcastServiceTestConfiguration; +import ru.spcex.clearing.company.utils.MatcherFactory; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.ActionType; +import ru.spcex.clearing.platform.messaging.domain.BaseRequest; +import ru.spcex.clearing.platform.messaging.domain.Consts; +import ru.spcex.clearing.platform.messaging.domain.cud.company.ContactUpdateRequest; +import ru.spcex.platform.imdg.iml.hazelcast.service.HazelcastService; + +import java.util.Collections; +import java.util.HashMap; + +import static ru.spcex.clearing.company.utils.MatcherFactory.usingIgnoringFieldsComparator; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + HazelcastServiceTestConfiguration.class}) +class ContactServiceTest { + + public static final MatcherFactory.Matcher CONTACT_MATCHER = usingIgnoringFieldsComparator(); + private static final int PARTITION = 0; + private static final String TOPIC_CONTACT_UPDATE = Consts.DESTINATION_CONTACT_UPDATE; + private static final Long ID = 0L; + + @Autowired + @Qualifier("hazelcastServiceTest") + private HazelcastService hazelcastServiceTest; + private MockConsumer mockConsumer; + + @BeforeEach + void setUp() { + mockConsumer = new MockConsumer<>(OffsetResetStrategy.EARLIEST); + } + + /** + * {@link ContactService#contactUpdate(BaseRequest)}
+ * Тест проверяет обновление сущности {@link Contact} в Hazelcast при передаче из Apache Kafka.
+ * Входной запрос {@link ContactUpdateRequest}:
+ * {@link ContactUpdateRequest#id} - Идентификатор записи
+ * {@link ContactUpdateRequest#companyId} - Идентификатор записи
+ * {@link ContactUpdateRequest#contactType} - 1234
+ * {@link ContactUpdateRequest#contactValue} - new ContactValue
+ */ + @Test + void contactUpdate() throws InterruptedException { + //ARRANGE + Contact existsContact = new Contact(); + existsContact.setId(ID); + existsContact.setCompanyId(ID); + existsContact.setContactType("0000"); + existsContact.setContactValue("exists ContactValue"); + + ContactUpdateRequest contactUpdateRequest = new ContactUpdateRequest(); + contactUpdateRequest.setId(ID); + contactUpdateRequest.setCompanyId(1L); + contactUpdateRequest.setContactType("1234"); + contactUpdateRequest.setContactValue("new ContactValue"); + + BaseRequest baseUpdateRequest = new BaseRequest<>(); + baseUpdateRequest.setRequestPayload(contactUpdateRequest); + baseUpdateRequest.setId(ID); + baseUpdateRequest.setActionType(ActionType.UPDATE); + String jsonBaseForUpdatingRequest; + ObjectMapper objectMapper = new ObjectMapper(); + try { + jsonBaseForUpdatingRequest = objectMapper.writeValueAsString(baseUpdateRequest); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + + Contact predictableContact = new Contact(); + predictableContact.setId(ID); + predictableContact.setCompanyId(ID); + predictableContact.setContactType("0000"); + predictableContact.setContactValue("new ContactValue"); + + //ACT + //service set up + ContactService contactService = new ContactService(mockConsumer, hazelcastServiceTest); + contactService.afterPropertiesSet(); + + //callbacks set up + IMap iMap = hazelcastServiceTest.getHazelcast().getMap(IMDGDistributedNames.Map_Contact); + iMap.put(ID, existsContact); + + //KAFKA + mockConsumer.schedulePollTask(() -> { + mockConsumer.rebalance(Collections.singletonList(new TopicPartition(TOPIC_CONTACT_UPDATE, PARTITION))); + mockConsumer.addRecord(new ConsumerRecord<>(TOPIC_CONTACT_UPDATE, PARTITION, 0, "key", jsonBaseForUpdatingRequest)); + }); + HashMap startOffsetsUpdating = new HashMap<>(); + TopicPartition tpUpdating = new TopicPartition(TOPIC_CONTACT_UPDATE, PARTITION); + startOffsetsUpdating.put(tpUpdating, 0L); + mockConsumer.updateBeginningOffsets(startOffsetsUpdating); + + //waiting for hazelcast map updates + Object waiter = new Object(); + String listenerID = iMap.addEntryListener((EntryUpdatedListener) entryEvent -> { + System.out.println("Checking If removed.."); + + try { + waiter.wait(100); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + synchronized (waiter) { + waiter.notify(); + } + }, false); + + synchronized (waiter) { + waiter.wait(100); + } + + //ASSERT + Contact resultUpdating = iMap.get(ID); + CONTACT_MATCHER.assertMatch(resultUpdating, predictableContact); + + //preparing hazelcastImdgProvider for next test + iMap.removeEntryListener(listenerID); + } +} \ No newline at end of file diff --git a/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/utils/MatcherFactory.java b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/utils/MatcherFactory.java new file mode 100644 index 000000000..e850b89ef --- /dev/null +++ b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/utils/MatcherFactory.java @@ -0,0 +1,38 @@ +package ru.spcex.clearing.company.utils; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Factory for creating test matchers. + *

+ * Comparing actual and expected objects via AssertJ + */ +public class MatcherFactory { + + public static Matcher usingIgnoringFieldsComparator(String... fieldsToIgnore) { + return new Matcher<>(fieldsToIgnore); + } + + public static class Matcher { + private final String[] fieldsToIgnore; + + private Matcher(String... fieldsToIgnore) { + this.fieldsToIgnore = fieldsToIgnore; + } + + public void assertMatch(T actual, T expected) { + assertThat(actual).usingRecursiveComparison().ignoringFields(fieldsToIgnore).isEqualTo(expected); + } + + @SafeVarargs + public final void assertMatch(Iterable actual, T... expected) { + assertMatch(actual, Arrays.asList(expected)); + } + + public void assertMatch(Iterable actual, Iterable expected) { + assertThat(actual).usingRecursiveFieldByFieldElementComparatorIgnoringFields(fieldsToIgnore).isEqualTo(expected); + } + } +}