diff --git a/pom.xml b/pom.xml index 9dca9683a298a672846b0d6b38c97b1fbc77390d..60f25f39cab6e984a59bf9c0d7379076299e7a09 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - biz.nynja.search + biz.nynja.search.grpc search-service 0.0.1-SNAPSHOT jar @@ -77,15 +77,31 @@ 1.6.1 + + org.springframework.boot + spring-boot-starter-data-cassandra + + + + com.datastax.cassandra + cassandra-driver-core + + + io.netty + netty-handler + + + + com.google.guava guava 21.0 - + libs-snapshot-local.biz.nynja.protos - search-intracoldev + search-service-intracoldev 1.0-SNAPSHOT @@ -95,6 +111,12 @@ + + com.googlecode.libphonenumber + libphonenumber + 8.9.12 + + com.fasterxml.jackson.core jackson-databind @@ -112,6 +134,15 @@ groovy-all + + io.micrometer + micrometer-core + + + + io.micrometer + micrometer-registry-prometheus + diff --git a/src/main/java/biz/nynja/search/configuration/SearchAccountConfig.java b/src/main/java/biz/nynja/search/configuration/SearchAccountConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..c9991771643c057a8245ce1eb1cf42253f6f28ec --- /dev/null +++ b/src/main/java/biz/nynja/search/configuration/SearchAccountConfig.java @@ -0,0 +1,35 @@ +package biz.nynja.search.configuration; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +@Configuration +public class SearchAccountConfig { + + private final String accountServiceAddress; + private final int accountServicePort; + +// @Autowired + public SearchAccountConfig(Environment env) { + this.accountServiceAddress = env.getRequiredProperty("services.account.address"); + this.accountServicePort = parseProperty(env, "services.account.port"); + } + + private int parseProperty(Environment env, String property) throws InternalError { + try { + return Integer.parseInt(env.getRequiredProperty(property)); + } catch (NumberFormatException ex) { + throw new InternalError("Integer expected in " + property); + } + } + + public String getAccountServiceAddress() { + return accountServiceAddress; + } + + public int getAccountServicePort() { + return accountServicePort; + } + +} diff --git a/src/main/java/biz/nynja/search/healthindicators/GrpcServerHealthIndicator.java b/src/main/java/biz/nynja/search/healthindicators/GrpcServerHealthIndicator.java index cb98dd5c38d694013dbe713904bfd28982841940..3e0c604377fed4598d2b5ab4ee24e45b5d0912af 100644 --- a/src/main/java/biz/nynja/search/healthindicators/GrpcServerHealthIndicator.java +++ b/src/main/java/biz/nynja/search/healthindicators/GrpcServerHealthIndicator.java @@ -13,8 +13,8 @@ import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; -import biz.nynja.search.grpc.AccountServiceGrpc; -import biz.nynja.search.services.AccountServiceImpl; +import biz.nynja.search.grpc.SearchServiceGrpc; +import biz.nynja.search.services.SearchServiceImpl; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.health.v1.HealthCheckRequest; @@ -27,7 +27,7 @@ import io.grpc.health.v1.HealthGrpc; @Component public class GrpcServerHealthIndicator implements HealthIndicator { - private static final Logger LOGGER = LoggerFactory.getLogger(AccountServiceImpl.class); + private static final Logger LOGGER = LoggerFactory.getLogger(SearchServiceImpl.class); @Autowired protected GRpcServerProperties gRpcServerProperties; @@ -37,14 +37,14 @@ public class GrpcServerHealthIndicator implements HealthIndicator { ManagedChannel channel = onChannelBuild( ManagedChannelBuilder.forAddress("localhost", gRpcServerProperties.getPort()).usePlaintext()).build(); final HealthCheckRequest healthCheckRequest = HealthCheckRequest.newBuilder() - .setService(AccountServiceGrpc.getServiceDescriptor().getName()).build(); + .setService(SearchServiceGrpc.getServiceDescriptor().getName()).build(); final HealthGrpc.HealthFutureStub healthFutureStub = HealthGrpc.newFutureStub(channel); HealthCheckResponse.ServingStatus servingStatus = null; try { servingStatus = healthFutureStub.check(healthCheckRequest).get().getStatus(); } catch (InterruptedException | ExecutionException e) { LOGGER.error("Error retrieving health status for {}: {}", - AccountServiceGrpc.getServiceDescriptor().getName(), e.getMessage()); + SearchServiceGrpc.getServiceDescriptor().getName(), e.getMessage()); LOGGER.debug(null, e.getCause()); return Health.down().build(); } diff --git a/src/main/java/biz/nynja/search/models/AccountInfo.java b/src/main/java/biz/nynja/search/models/AccountInfo.java deleted file mode 100644 index 817cba1fb8af17a83a30bebc182306cdd8999b2d..0000000000000000000000000000000000000000 --- a/src/main/java/biz/nynja/search/models/AccountInfo.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (C) 2018 Nynja Inc. All rights reserved. - */ -package biz.nynja.search.models; - -import org.springframework.data.cassandra.core.mapping.PrimaryKey; -import org.springframework.data.cassandra.core.mapping.Table; - -import biz.nynja.search.grpc.RegisterRequest; - -/** - * Account bean as represented in the corresponding Cassandra table. - */ -@Table -public class AccountInfo { - - @PrimaryKey - private Long id; - private String email; - private String password; - - public AccountInfo() { - } - - public AccountInfo(long id, String email, String password) { - this.id = id; - this.email = email; - this.password = password; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - @Override - public String toString() { - return "AccountInfo [id=" + id + ", email=" + email + ", password=" + password + "]"; - } - - public static AccountInfo fromProto(RegisterRequest proto) { - AccountInfo accountInfo = new AccountInfo(); - accountInfo.setId(proto.getId()); - accountInfo.setEmail(proto.getEmail()); - accountInfo.setPassword(proto.getPassword()); - return accountInfo; - } - - public biz.nynja.blueprint.grpc.AccountInfo toProto() { - return biz.nynja.blueprint.grpc.AccountInfo.newBuilder().setId(Long.toString(getId())).setEmail(getEmail()) - .build(); - } -} diff --git a/src/main/java/biz/nynja/search/repositories/AccountInfoRepository.java b/src/main/java/biz/nynja/search/repositories/AccountInfoRepository.java deleted file mode 100644 index bce17d84fd0e47f4c845bd6dd7f32d028c6036ed..0000000000000000000000000000000000000000 --- a/src/main/java/biz/nynja/search/repositories/AccountInfoRepository.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (C) 2018 Nynja Inc. All rights reserved. - */ -package biz.nynja.search.repositories; - -import org.springframework.data.cassandra.repository.Query; -import org.springframework.data.repository.CrudRepository; - -import biz.nynja.search.models.AccountInfo; - -/** - * Account repository for Cassandra operations/queries. - */ -public interface AccountInfoRepository extends CrudRepository { - - @Query(value = "SELECT * FROM AccountInfo WHERE email=?0 ALLOW FILTERING") - public AccountInfo findByEmail(String email); -} diff --git a/src/main/java/biz/nynja/search/services/AccountServiceCommunicator.java b/src/main/java/biz/nynja/search/services/AccountServiceCommunicator.java new file mode 100644 index 0000000000000000000000000000000000000000..41185578465f27e5ac1c1a437e106d96279f7553 --- /dev/null +++ b/src/main/java/biz/nynja/search/services/AccountServiceCommunicator.java @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.search.services; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import biz.nynja.search.grpc.SearchServiceGrpc; +import biz.nynja.search.configuration.SearchAccountConfig; +import biz.nynja.search.grpc.SearchByEmailRequest; +import biz.nynja.search.grpc.SearchByPhoneNumberRequest; +import biz.nynja.search.grpc.SearchByQrCodeRequest; +import biz.nynja.search.grpc.SearchByUsernameRequest; +import biz.nynja.search.grpc.SearchResponse; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; + +@Service +public class AccountServiceCommunicator { + + @Autowired + private SearchAccountConfig searchAccountConfig; + + public SearchResponse searchByUsername(String username) { + SearchByUsernameRequest request = SearchByUsernameRequest.newBuilder().setUsername(username).build(); + + ManagedChannel managedChannel = ManagedChannelBuilder.forAddress(searchAccountConfig.getAccountServiceAddress(), searchAccountConfig.getAccountServicePort()) + .usePlaintext().build(); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(managedChannel); + + SearchResponse response = searchServiceBlockingStub.searchByUsername(request); + return response; + } + + public SearchResponse searchByPhoneNumber(String phoneNumber) { + SearchByPhoneNumberRequest request = SearchByPhoneNumberRequest.newBuilder().setPhoneNumber(phoneNumber) + .build(); + + ManagedChannel managedChannel = ManagedChannelBuilder.forAddress(searchAccountConfig.getAccountServiceAddress(), searchAccountConfig.getAccountServicePort()) + .usePlaintext().build(); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(managedChannel); + + SearchResponse response = searchServiceBlockingStub.searchByPhoneNumber(request); + return response; + } + + public SearchResponse searchByEmail(String email) { + SearchByEmailRequest request = SearchByEmailRequest.newBuilder().setEmail(email).build(); + + ManagedChannel managedChannel = ManagedChannelBuilder.forAddress(searchAccountConfig.getAccountServiceAddress(), searchAccountConfig.getAccountServicePort()) + .usePlaintext().build(); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(managedChannel); + + SearchResponse response = searchServiceBlockingStub.searchByEmail(request); + return response; + } + + public SearchResponse searchByQrCode(String qrcode) { + SearchByQrCodeRequest request = SearchByQrCodeRequest.newBuilder().setQrCode(qrcode).build(); + + ManagedChannel managedChannel = ManagedChannelBuilder.forAddress(searchAccountConfig.getAccountServiceAddress(), searchAccountConfig.getAccountServicePort()) + .usePlaintext().build(); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(managedChannel); + + SearchResponse response = searchServiceBlockingStub.searchByQrCode(request); + return response; + } + +} diff --git a/src/main/java/biz/nynja/search/services/AccountServiceImpl.java b/src/main/java/biz/nynja/search/services/AccountServiceImpl.java deleted file mode 100644 index 665ee36242d20267a5e2441f83dbc4cd5a17262e..0000000000000000000000000000000000000000 --- a/src/main/java/biz/nynja/search/services/AccountServiceImpl.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (C) 2018 Nynja Inc. All rights reserved. - */ -package biz.nynja.search.services; - -import org.lognet.springboot.grpc.GRpcService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; - -import biz.nynja.search.grpc.AccountServiceGrpc; -import biz.nynja.search.grpc.RegisterError; -import biz.nynja.search.grpc.RegisterRequest; -import biz.nynja.search.grpc.RegisterResponse; -import biz.nynja.search.grpc.RegisterError.Cause; -import biz.nynja.search.models.AccountInfo; -import biz.nynja.search.repositories.AccountInfoRepository; -import io.grpc.stub.StreamObserver; - -/** - * gRPC Account service implementation.
- * The service extends the protobuf generated class and overrides the needed methods. It also saves/retrieves the - * account information via {@link AccountInfoRepository}. - */ -@GRpcService -public class AccountServiceImpl extends AccountServiceGrpc.AccountServiceImplBase { - - private static final Logger logger = LoggerFactory.getLogger(AccountServiceImpl.class); - - @Autowired - private AccountInfoRepository accountInfoRepository; - - @Override - public void register(RegisterRequest request, StreamObserver responseObserver) { - - logger.info("Got a gRPC service request: {}", request); - - AccountInfo accountInfo = AccountInfo.fromProto(request); - - if (accountInfoRepository.findByEmail(accountInfo.getEmail()) != null) { - responseObserver.onNext(RegisterResponse.newBuilder() - .setError(RegisterError.newBuilder().setCause(Cause.EMAIL_ALREADY_USED)).build()); - responseObserver.onCompleted(); - return; - } - - AccountInfo savedAccount = accountInfoRepository.save(accountInfo); - logger.info("Account \"{}\" saved into the DB", savedAccount.toString()); - - RegisterResponse response = RegisterResponse.newBuilder().setAccount(savedAccount.toProto()).build(); - logger.info("Response from gRPC service: \"{}\"", response); - - responseObserver.onNext(response); - responseObserver.onCompleted(); - } -} diff --git a/src/main/java/biz/nynja/search/services/SearchServiceImpl.java b/src/main/java/biz/nynja/search/services/SearchServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..d0c67cf324db6a3ac3149e87065b41d2d6ccae19 --- /dev/null +++ b/src/main/java/biz/nynja/search/services/SearchServiceImpl.java @@ -0,0 +1,130 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.search.services; + +import org.lognet.springboot.grpc.GRpcService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import biz.nynja.search.grpc.SearchServiceGrpc; +import biz.nynja.search.grpc.ErrorResponse; +import biz.nynja.search.grpc.ErrorResponse.Cause; +import biz.nynja.search.grpc.SearchByEmailRequest; +import biz.nynja.search.grpc.SearchByPhoneNumberRequest; +import biz.nynja.search.grpc.SearchByQrCodeRequest; +import biz.nynja.search.grpc.SearchByUsernameRequest; +import biz.nynja.search.grpc.SearchResponse; +import io.grpc.stub.StreamObserver; + +/** + * gRPC Search service implementation.
+ * The service extends the protobuf generated class and overrides the needed methods. + * It also saves/retrieves the account information via the Account service. + */ +@GRpcService +public class SearchServiceImpl extends SearchServiceGrpc.SearchServiceImplBase { + + private static final Logger logger = LoggerFactory.getLogger(SearchServiceImpl.class); + + @Autowired + private AccountServiceCommunicator accountService; + + @Override + public void searchByUsername(SearchByUsernameRequest request, StreamObserver responseObserver) { + + logger.info("Searching account by username {}", request.getUsername()); + if ((request.getUsername() == null) || request.getUsername().isEmpty()) { + logAndBuildGrpcResponse(responseObserver, "Missing username: {}", Cause.MISSING_USERNAME); + return; + } + + SearchResponse response = accountService.searchByUsername(request.getUsername()); + + if ((response.getError() != null) && (response.getError().getCauseValue() != 0)) { + logAndBuildGrpcResponse(responseObserver, "Account service returns an error with the cause: {}", response.getError().getCause()); + return; + } + + logger.debug("Found result for search by username {}: \"{}\"", request.getUsername(), response); + responseObserver.onNext(response); + responseObserver.onCompleted(); + return; + } + + @Override + public void searchByPhoneNumber(SearchByPhoneNumberRequest request, + StreamObserver responseObserver) { + + logger.info("Searching account by phone number {}", request.getPhoneNumber()); + if ((request.getPhoneNumber() == null) || request.getPhoneNumber().isEmpty()) { + logAndBuildGrpcResponse(responseObserver, "Missing phone number: {}", Cause.MISSING_PHONENUMBER); + return; + } + + SearchResponse response = accountService.searchByPhoneNumber(request.getPhoneNumber()); + + if (response.getError().getCauseValue() != 0) { + logAndBuildGrpcResponse(responseObserver, "Account service returns an error with the cause: {}", response.getError().getCause()); + return; + } + + logger.debug("Found result for search by phone number {}: \"{}\"", request.getPhoneNumber(), response); + responseObserver.onNext(response); + responseObserver.onCompleted(); + return; + } + + @Override + public void searchByEmail(SearchByEmailRequest request, StreamObserver responseObserver) { + + logger.info("Searching account by e-mail {}", request.getEmail()); + if ((request.getEmail() == null) || request.getEmail().isEmpty()) { + logAndBuildGrpcResponse(responseObserver, "Missing e-mail: {}", Cause.MISSING_EMAIL); + return; + } + + SearchResponse response = accountService.searchByEmail(request.getEmail()); + + if (response.getError().getCauseValue() != 0) { + logAndBuildGrpcResponse(responseObserver, "Account service returns an error with the cause: {}", response.getError().getCause()); + return; + } + + logger.debug("Found result for search by phone number {}: \"{}\"", request.getEmail(), response); + responseObserver.onNext(response); + responseObserver.onCompleted(); + return; + } + + @Override + public void searchByQrCode(SearchByQrCodeRequest request, StreamObserver responseObserver) { + + logger.info("Searching account by QR code {}", request.getQrCode()); + if ((request.getQrCode() == null) || request.getQrCode().isEmpty()) { + logAndBuildGrpcResponse(responseObserver, "Missing QR code: {}", Cause.MISSING_QR_CODE); + return; + } + + SearchResponse response = accountService.searchByQrCode(request.getQrCode()); + + if (response.getError().getCauseValue() != 0) { + logAndBuildGrpcResponse(responseObserver, "Account service returns an error with the cause: {}", response.getError().getCause()); + return; + } + + logger.debug("Found result for search by phone number {}: \"{}\"", request.getQrCode(), response); + responseObserver.onNext(response); + responseObserver.onCompleted(); + return; + } + + private static void logAndBuildGrpcResponse(StreamObserver responseObserver, String logMessage, Cause cause) { + logger.debug(logMessage, cause.toString()); + responseObserver.onNext(SearchResponse.newBuilder() + .setError(ErrorResponse.newBuilder().setCause(cause)).build()); + responseObserver.onCompleted(); + } + +} \ No newline at end of file diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index d33d2617d00f9e3406846346ee35e50eaa6ef94d..b8525f8032d65d055c0f7ab26c53f5b0c6af5ded 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,7 +1,17 @@ grpc: enabled: true enableReflection: false - port: 6568 - + port: 6567 + unitTest: + port: + searchServiceTest: 10002 + + +services: + account: + address: 127.0.0.1 + port: 6565 + server: - port: 8084 \ No newline at end of file + port: 8018 + diff --git a/src/main/resources/application-production.yml b/src/main/resources/application-production.yml deleted file mode 100644 index 6280131fe1751248d5ef556573d7b45486d457e5..0000000000000000000000000000000000000000 --- a/src/main/resources/application-production.yml +++ /dev/null @@ -1,7 +0,0 @@ -grpc: - enabled: ${GRPC_ENABLED:true} - enableReflection: ${GRPC_ENABLE_REFLECTION:false} - port: ${GRPC_SERVER_PORT:6568} - -server: - port: ${APPLICATION_SERVER_PORT:8084} \ No newline at end of file diff --git a/src/main/resources/logback-spring.groovy b/src/main/resources/logback-spring.groovy index 82f9fa7c51bf4b0a5ecfca69fca4c716782c658c..5c01a5bf2229222617492a3768a9eb3e9e6dc2c6 100644 --- a/src/main/resources/logback-spring.groovy +++ b/src/main/resources/logback-spring.groovy @@ -3,6 +3,7 @@ */ import ch.qos.logback.classic.encoder.PatternLayoutEncoder +import ch.qos.logback.core.ConsoleAppender import ch.qos.logback.core.rolling.RollingFileAppender import ch.qos.logback.core.rolling.TimeBasedRollingPolicy import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP @@ -11,7 +12,7 @@ import ch.qos.logback.classic.Level statusListener(OnConsoleStatusListener) -def file = "${System.getProperty('log.dir', '')}blueprint-java-spring-service-%d.%i.log" +def file = "${System.getProperty('log.dir', '')}search-service-%d.%i.log" appender("FILE", RollingFileAppender) { // add a status message regarding the file property @@ -23,6 +24,10 @@ appender("FILE", RollingFileAppender) { timeBasedFileNamingAndTriggeringPolicy(SizeAndTimeBasedFNATP) { maxFileSize = "10mb" }} } -logger("org.springframework.cloud.config.client.ConfigServicePropertySourceLocator", Level.WARN) +appender("Console-Appender", ConsoleAppender) { + encoder(PatternLayoutEncoder) { pattern = "%d{HH:mm:ss.SSS} %level %logger - %msg%n" } +} +logger("org.springframework.cloud.config.client.ConfigServicePropertySourceLocator", Level.WARN) +root(INFO, ["Console-Appender"]) root(Level.toLevel("${System.getProperty('log.level', 'INFO')}"), ["FILE"]) \ No newline at end of file diff --git a/src/test/java/biz/nynja/search/ApplicationTests.java b/src/test/java/biz/nynja/search/ApplicationTests.java index 9ae2bf56333393d9644a88b82a827d99b6941a1b..8ad63677b81957dfe2fec5a7850541cf70fd3e2a 100644 --- a/src/test/java/biz/nynja/search/ApplicationTests.java +++ b/src/test/java/biz/nynja/search/ApplicationTests.java @@ -3,6 +3,14 @@ */ package biz.nynja.search; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.BDDMockito.given; + +import java.util.Optional; +import java.util.concurrent.ExecutionException; + +import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -11,65 +19,294 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; -import biz.nynja.search.models.AccountInfo; -import biz.nynja.search.repositories.AccountInfoRepository; +import biz.nynja.search.grpc.ErrorResponse.Cause; +import biz.nynja.search.grpc.ErrorResponse; +import biz.nynja.search.grpc.SearchByEmailRequest; +import biz.nynja.search.grpc.SearchByPhoneNumberRequest; +import biz.nynja.search.grpc.SearchByQrCodeRequest; +import biz.nynja.search.grpc.SearchByUsernameRequest; +import biz.nynja.search.grpc.SearchResponse; +import biz.nynja.search.grpc.SearchServiceGrpc; +import biz.nynja.search.services.AccountServiceCommunicator; @RunWith(SpringRunner.class) @SpringBootTest(classes = Util.class, - properties = { + properties = { "grpc.port=${grpc.unitTest.port.searchServiceTest}", "spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration", "spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration" }) @ActiveProfiles("dev") public class ApplicationTests extends GrpcServerTestBase { - @MockBean - private AccountInfoRepository accountInfoRepository; - - @Autowired - @Qualifier("newAccount") - private AccountInfo accountInfo; - - @Autowired - @Qualifier("savedAccount") - private AccountInfo savedAccount; - // - // @Test - // public void testRegister() throws ExecutionException, InterruptedException { - // - // String testEmail = "email@test.com"; - // final RegisterRequest request = RegisterRequest.newBuilder().setId(1).setEmail(testEmail).setPassword("abc") - // .build(); - // - // given(accountInfoRepository.findByEmail(testEmail)).willReturn(null); - // given(accountInfoRepository.save(any(AccountInfo.class))).willReturn(savedAccount); - // - // final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc - // .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); - // - // final RegisterResponse reply = accountServiceBlockingStub.register(request); - // - // assertNotNull("Reply should not be null", reply); - // assertTrue(String.format("Reply should contain email '%s'", testEmail), - // reply.getAccount().getEmail().equals(testEmail)); - // } - // - // @Test - // public void testRegisterExistEmail() throws ExecutionException, InterruptedException { - // - // String testEmail = "email@test.com"; - // final RegisterRequest request = RegisterRequest.newBuilder().setId(1).setEmail(testEmail).setPassword("abc") - // .build(); - // - // given(accountInfoRepository.findByEmail(testEmail)).willReturn(accountInfo); - // - // final AccountServiceGrpc.AccountServiceBlockingStub accountServiceBlockingStub = AccountServiceGrpc - // .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); - // - // final RegisterResponse reply = accountServiceBlockingStub.register(request); - // - // assertNotNull("Reply should not be null", reply); - // assertTrue(String.format("Reply should contain cause '%s'", testEmail), - // reply.getError().getCause().equals(RegisterError.Cause.EMAIL_ALREADY_USED)); - // } + @MockBean + private AccountServiceCommunicator accountService; + + @Autowired + @Qualifier("savedResponse") + private SearchResponse savedResponse; + + @Test + public void testSearchByUsername() throws ExecutionException, InterruptedException { + final SearchByUsernameRequest request = SearchByUsernameRequest.newBuilder() + .setUsername(Util.USERNAME).build(); + + SearchResponse response = savedResponse; + + given(accountService.searchByUsername(request.getUsername())).willReturn(response); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByUsername(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain last name '%s'", Util.LAST_NAME.toString()), + reply.getSearchResultDetails().getLastName().equals(Util.LAST_NAME.toString())); + } + + @Test + public void testSearchByUsernameNotFound() throws ExecutionException, InterruptedException { + final SearchByUsernameRequest request = SearchByUsernameRequest.newBuilder() + .setUsername(Util.USERNAME).build(); + + SearchResponse response = SearchResponse.newBuilder().setError((ErrorResponse.newBuilder().setCause(Cause.USERNAME_NOT_FOUND))).build(); + + given(accountService.searchByUsername(request.getUsername())).willReturn(response); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByUsername(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", biz.nynja.search.grpc.ErrorResponse.Cause.USERNAME_NOT_FOUND), + reply.getError().getCause().equals(biz.nynja.search.grpc.ErrorResponse.Cause.USERNAME_NOT_FOUND)); + } + + @Test + public void testSearchByUsernameBadRequest() throws ExecutionException, InterruptedException { + final SearchByUsernameRequest request = SearchByUsernameRequest.newBuilder().build(); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByUsername(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_USERNAME), + reply.getError().getCause().equals(Cause.MISSING_USERNAME)); + } + + @Test + public void testSearchByUsernameInvalid() { + final SearchByUsernameRequest request = SearchByUsernameRequest.newBuilder() + .setUsername(Util.INVALID_USERNAME).build(); + + SearchResponse response = SearchResponse.newBuilder().setError((ErrorResponse.newBuilder().setCause(Cause.INVALID_USERNAME))).build(); + + given(accountService.searchByUsername(request.getUsername())).willReturn(response); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByUsername(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.INVALID_USERNAME), + reply.getError().getCause().equals(Cause.INVALID_USERNAME)); + } + + @Test + public void testSearchByPhoneNumber() throws ExecutionException, InterruptedException { + final SearchByPhoneNumberRequest request = SearchByPhoneNumberRequest.newBuilder() + .setPhoneNumber(Util.PHONE_NUMBER).build(); + + SearchResponse response = savedResponse; + + given(accountService.searchByPhoneNumber(request.getPhoneNumber())).willReturn(response); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByPhoneNumber(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain last name '%s'", Util.LAST_NAME.toString()), + reply.getSearchResultDetails().getLastName().equals(Util.LAST_NAME.toString())); + } + + @Test + public void testSearchByPhoneNumberNotFound() throws ExecutionException, InterruptedException { + final SearchByPhoneNumberRequest request = SearchByPhoneNumberRequest.newBuilder() + .setPhoneNumber(Util.PHONE_NUMBER).build(); + + SearchResponse response = SearchResponse.newBuilder().setError((ErrorResponse.newBuilder().setCause(Cause.PHONENUMBER_NOT_FOUND))).build(); + + given(accountService.searchByPhoneNumber(request.getPhoneNumber())).willReturn(response); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByPhoneNumber(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", biz.nynja.search.grpc.ErrorResponse.Cause.PHONENUMBER_NOT_FOUND), + reply.getError().getCause().equals(biz.nynja.search.grpc.ErrorResponse.Cause.PHONENUMBER_NOT_FOUND)); + } + + @Test + public void testSearchByPhoneNumberBadRequest() throws ExecutionException, InterruptedException { + final SearchByPhoneNumberRequest request = SearchByPhoneNumberRequest.newBuilder().build(); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByPhoneNumber(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_PHONENUMBER), + reply.getError().getCause().equals(Cause.MISSING_PHONENUMBER)); + } + + @Test + public void testSearchByPhoneNumberInvalid() { + final SearchByPhoneNumberRequest request = SearchByPhoneNumberRequest.newBuilder() + .setPhoneNumber(Util.INVALID_PHONENUMBER).build(); + + SearchResponse response = SearchResponse.newBuilder().setError((ErrorResponse.newBuilder().setCause(Cause.INVALID_PHONENUMBER))).build(); + + given(accountService.searchByPhoneNumber(request.getPhoneNumber())).willReturn(response); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByPhoneNumber(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.INVALID_PHONENUMBER), + reply.getError().getCause().equals(Cause.INVALID_PHONENUMBER)); + } + + @Test + public void testSearchByEmail() throws ExecutionException, InterruptedException { + final SearchByEmailRequest request = SearchByEmailRequest.newBuilder() + .setEmail(Util.EMAIL).build(); + + SearchResponse response = savedResponse; + + given(accountService.searchByEmail(request.getEmail())).willReturn(response); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByEmail(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain last name '%s'", Util.LAST_NAME.toString()), + reply.getSearchResultDetails().getLastName().equals(Util.LAST_NAME.toString())); + } + + @Test + public void testSearchByEmailNotFound() throws ExecutionException, InterruptedException { + final SearchByEmailRequest request = SearchByEmailRequest.newBuilder() + .setEmail(Util.EMAIL).build(); + + SearchResponse response = SearchResponse.newBuilder().setError((ErrorResponse.newBuilder().setCause(Cause.EMAIL_NOT_FOUND))).build(); + + given(accountService.searchByEmail(request.getEmail())).willReturn(response); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByEmail(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", biz.nynja.search.grpc.ErrorResponse.Cause.EMAIL_NOT_FOUND), + reply.getError().getCause().equals(biz.nynja.search.grpc.ErrorResponse.Cause.EMAIL_NOT_FOUND)); + } + + @Test + public void testSearchByEmailBadRequest() throws ExecutionException, InterruptedException { + final SearchByEmailRequest request = SearchByEmailRequest.newBuilder().build(); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByEmail(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_EMAIL), + reply.getError().getCause().equals(Cause.MISSING_EMAIL)); + } + + @Test + public void testSearchByEmailInvalid() { + final SearchByEmailRequest request = SearchByEmailRequest.newBuilder() + .setEmail(Util.INVALID_EMAIL).build(); + + SearchResponse response = SearchResponse.newBuilder().setError((ErrorResponse.newBuilder().setCause(Cause.INVALID_EMAIL))).build(); + + given(accountService.searchByEmail(request.getEmail())).willReturn(response); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByEmail(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.INVALID_EMAIL), + reply.getError().getCause().equals(Cause.INVALID_EMAIL)); + } + + @Test + public void testSearchByQrCode() throws ExecutionException, InterruptedException { + final SearchByQrCodeRequest request = SearchByQrCodeRequest.newBuilder() + .setQrCode(Util.QR_CODE).build(); + + SearchResponse response = savedResponse; + + given(accountService.searchByQrCode(request.getQrCode())).willReturn(response); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByQrCode(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain last name '%s'", Util.LAST_NAME.toString()), + reply.getSearchResultDetails().getLastName().equals(Util.LAST_NAME.toString())); + } + + @Test + public void testSearchByQrCodeNotFound() throws ExecutionException, InterruptedException { + final SearchByQrCodeRequest request = SearchByQrCodeRequest.newBuilder() + .setQrCode(Util.QR_CODE).build(); + + SearchResponse response = SearchResponse.newBuilder().setError((ErrorResponse.newBuilder().setCause(Cause.QR_CODE_NOT_FOUND))).build(); + + given(accountService.searchByQrCode(request.getQrCode())).willReturn(response); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByQrCode(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", biz.nynja.search.grpc.ErrorResponse.Cause.QR_CODE_NOT_FOUND), + reply.getError().getCause().equals(biz.nynja.search.grpc.ErrorResponse.Cause.QR_CODE_NOT_FOUND)); + } + + @Test + public void testSearchByQrCodeBadRequest() throws ExecutionException, InterruptedException { + final SearchByQrCodeRequest request = SearchByQrCodeRequest.newBuilder().build(); + + final SearchServiceGrpc.SearchServiceBlockingStub searchServiceBlockingStub = SearchServiceGrpc + .newBlockingStub(Optional.ofNullable(channel).orElse(inProcChannel)); + + final SearchResponse reply = searchServiceBlockingStub.searchByQrCode(request); + + assertNotNull("Reply should not be null", reply); + assertTrue(String.format("Reply should contain cause '%s'", Cause.MISSING_QR_CODE), + reply.getError().getCause().equals(Cause.MISSING_QR_CODE)); + } } diff --git a/src/test/java/biz/nynja/search/Util.java b/src/test/java/biz/nynja/search/Util.java index 8bf2aaed0f7c000d07d457b51755d2d1cd6a6c30..9b9f255dd2bb4fda0187ce2771d003a7ebd9dcdb 100644 --- a/src/test/java/biz/nynja/search/Util.java +++ b/src/test/java/biz/nynja/search/Util.java @@ -1,32 +1,46 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ + package biz.nynja.search; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; -import biz.nynja.search.models.AccountInfo;; +import com.google.protobuf.ByteString; + +import biz.nynja.search.grpc.SearchResponse; +import biz.nynja.search.grpc.SearchResultDetails; + +/** + * Unit tests variables, beans and help methods. + */ @TestConfiguration public class Util { - public static final Long ID = 1L; - public static final String EMAIL = "email@test.com"; - public static final String PASSWORD = "abc"; + public static final String USERNAME = "TomJohns"; + public static final String PHONE_NUMBER = "GB:448873598834"; + public static final String EMAIL = "p_petrov@abv.bg"; + public static final String QR_CODE = "QRcoded"; + public static final String INVALID_USERNAME = "Tom-Johns"; + public static final String INVALID_PHONENUMBER = "GB:44887359883"; // one DTMF less + public static final String INVALID_EMAIL = "p_petrov.@abv.bg"; - @Bean - public AccountInfo newAccount() { - AccountInfo accountInfo = new AccountInfo(); - accountInfo.setId(ID); - accountInfo.setEmail(EMAIL); - accountInfo.setPassword(PASSWORD); - return accountInfo; - } + public static final String AVATAR_STRING = "avatar-TomJohns"; + public static final String FIRST_NAME = "Tom"; + public static final String LAST_NAME = "Johns"; @Bean - public AccountInfo savedAccount() { - AccountInfo accountInfo = new AccountInfo(); - accountInfo.setId(ID); - accountInfo.setEmail(EMAIL); - accountInfo.setPassword(PASSWORD); - return accountInfo; + public SearchResponse savedResponse() { + SearchResultDetails searchResultDetails = SearchResultDetails.newBuilder() + .setAvatar(ByteString.copyFrom(Util.AVATAR_STRING.getBytes())) + .setFirstName(Util.FIRST_NAME) + .setLastName(Util.LAST_NAME) + .build(); + SearchResponse response = SearchResponse.newBuilder() + .setSearchResultDetails(searchResultDetails).build(); + return response; } + }