All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.grpcmock.GrpcMock Maven / Gradle / Ivy

There is a newer version: 0.14.0
Show newest version
package org.grpcmock;

import static java.util.Optional.ofNullable;

import io.grpc.MethodDescriptor;
import io.grpc.MethodDescriptor.MethodType;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.ServerInterceptor;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import io.grpc.stub.StreamObserver;
import io.grpc.util.MutableHandlerRegistry;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import org.grpcmock.definitions.response.Delay;
import org.grpcmock.definitions.response.ExceptionResponseActionBuilderImpl;
import org.grpcmock.definitions.response.ObjectResponseActionBuilderImpl;
import org.grpcmock.definitions.response.ResponseAction;
import org.grpcmock.definitions.response.StreamResponseBuilderImpl;
import org.grpcmock.definitions.response.steps.ExceptionResponseActionBuilder;
import org.grpcmock.definitions.response.steps.ExceptionStreamResponseBuildersStep;
import org.grpcmock.definitions.response.steps.ObjectResponseActionBuilder;
import org.grpcmock.definitions.response.steps.ObjectStreamResponseBuilderStep;
import org.grpcmock.definitions.stub.MethodStub;
import org.grpcmock.definitions.stub.ServerStreamingMethodStubBuilderImpl;
import org.grpcmock.definitions.stub.ServiceStub;
import org.grpcmock.definitions.stub.UnaryMethodStubBuilderImpl;
import org.grpcmock.definitions.stub.steps.BidiStreamingMethodStubBuilderStep;
import org.grpcmock.definitions.stub.steps.ClientStreamingMethodStubBuilderStep;
import org.grpcmock.definitions.stub.steps.MethodStubBuilder;
import org.grpcmock.definitions.stub.steps.ServerStreamingMethodStubBuilderStep;
import org.grpcmock.definitions.stub.steps.UnaryMethodStubBuilderStep;
import org.grpcmock.definitions.verification.CountMatcher;
import org.grpcmock.definitions.verification.RequestPattern;
import org.grpcmock.definitions.verification.RequestPatternBuilderImpl;
import org.grpcmock.definitions.verification.steps.RequestPatternBuilderStep;
import org.grpcmock.exception.GrpcMockException;
import org.grpcmock.exception.GrpcMockVerificationError;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Main gRPC Mock class for managing the gRPC server.
 *
 * @author Fadelis
 */
public final class GrpcMock {

  private static final Logger log = LoggerFactory.getLogger(GrpcMock.class);
  private static final ThreadLocal INSTANCE = ThreadLocal
      .withInitial(() -> grpcMock().build());

  private final Server server;
  private final MutableHandlerRegistry handlerRegistry;
  private final Map serviceStubs = new ConcurrentHashMap<>();

  GrpcMock(@Nonnull Server server, @Nonnull MutableHandlerRegistry handlerRegistry) {
    Objects.requireNonNull(server);
    Objects.requireNonNull(handlerRegistry);
    this.server = server;
    this.handlerRegistry = handlerRegistry;
  }

  /**
   * Retrieve current port of the server.
   *
   * @throws IllegalStateException if the server has not yet been started.
   */
  public int getPort() {
    return server.getPort();
  }

  /**
   * Starts the gRPC mock server. Does not throw any exception if the server is already running.
   *
   * @throws GrpcMockException if the server is unable to start.
   */
  public GrpcMock start() {
    try {
      server.start();
    } catch (IllegalStateException e) {
      log.warn("gRPC Mock server is already started");
    } catch (IOException e) {
      throw new GrpcMockException("failed to start gRPC Mock server", e);
    }
    return this;
  }

  /**
   * Stops the gRPC mock server via {@link Server#shutdownNow}.
   */
  public GrpcMock stop() {
    server.shutdownNow();
    return this;
  }

  /**
   * 

Register a gRPC method stub to the server. *

If given method is already registered, then configured scenarios will be appended to * that method's stub. */ public void register(@Nonnull MethodStubBuilder methodStubBuilder) { Objects.requireNonNull(methodStubBuilder); MethodStub methodStub = methodStubBuilder.build(); // register method stub to existing service stub or create a new one with the single method stub ServiceStub serviceStub = serviceStubs.compute( methodStub.serviceName(), (key, registeredStub) -> ofNullable(registeredStub) .map(previous -> previous.registerMethod(methodStub)) .orElseGet(() -> new ServiceStub(methodStub))); // create or overwrite the service definition for this service stub in the grpc server handlerRegistry.addService(serviceStub.serverServiceDefinition()); } /** * Verify that given {@link RequestPattern} is called a number of times satisfying the provided * {@link CountMatcher}. * * @throws GrpcMockVerificationError if the verify step fails. */ public void verifyThat( @Nonnull RequestPattern requestPattern, @Nonnull CountMatcher countMatcher ) { Objects.requireNonNull(requestPattern); Objects.requireNonNull(countMatcher); int callCount = ofNullable(serviceStubs.get(requestPattern.serviceName())) .map(serviceStub -> serviceStub.callCountFor(requestPattern)) .orElseThrow(() -> new GrpcMockException("No stub found for service: " + requestPattern.serviceName())); if (!countMatcher.test(callCount)) { throw new GrpcMockVerificationError(String.format( "Expected %s method to be called %s, but actual call count was %d", requestPattern.fullMethodName(), countMatcher, callCount)); } } /** * Removes all stubs defined from the mock server. */ public void resetAll() { serviceStubs.clear(); handlerRegistry.getServices().forEach(handlerRegistry::removeService); } /** * Returns gRPC Mock builder initiated with a random port. */ public static GrpcMockBuilder grpcMock() { return grpcMock(0); } /** * Returns gRPC Mock builder with the given port. If given port is 0, then a random * free port will be selected. */ public static GrpcMockBuilder grpcMock(int port) { return new GrpcMockBuilder(port); } /** * Returns gRPC Mock builder using the provided gRPC {@link ServerBuilder} configuration. The user * is responsible that the port used in the builder is available and free. */ public static GrpcMockBuilder grpcMock(@Nonnull ServerBuilder serverBuilder) { return new GrpcMockBuilder(serverBuilder); } /** * Configure the global static gRPC Mock instance to use a new one with the provided port. */ public static void configureFor(int port) { INSTANCE.set(grpcMock(port).build()); } /** * Configure the global static gRPC Mock instance to use the provided one. */ public static void configureFor(@Nonnull GrpcMock client) { Objects.requireNonNull(client); INSTANCE.set(client); } /** * Returns the port for the global static gRPC Mock instance. * * @throws IllegalStateException if the server has not yet been started. */ public static int getGlobalPort() { return INSTANCE.get().getPort(); } /** * Removes all stubs defined from the global mock server. */ public static void resetMappings() { INSTANCE.get().resetAll(); } /** *

Register a gRPC method stub to the global gRPC mock server. *

If given method is already registered, then configured scenarios will be appended to * that method's stub. *

When multiple stubs, satisfying the same request condition matching, are registered, the * last one registered will be triggered. * * @param methodStubBuilder a method stub builder created through one of {@link #unaryMethod}, * {@link #serverStreamingMethod}, {@link #clientStreamingMethod} or {@link * #bidiStreamingMethod}. */ public static void stubFor(MethodStubBuilder methodStubBuilder) { INSTANCE.get().register(methodStubBuilder); } /** * Returns a stub builder for {@link MethodType#UNARY} method or {@link * MethodType#SERVER_STREAMING} method with a single response. */ public static UnaryMethodStubBuilderStep unaryMethod( @Nonnull MethodDescriptor method) { return new UnaryMethodStubBuilderImpl<>(method); } /** * Returns a stub builder for {@link MethodType#SERVER_STREAMING} method. */ public static ServerStreamingMethodStubBuilderStep serverStreamingMethod( @Nonnull MethodDescriptor method) { return new ServerStreamingMethodStubBuilderImpl<>(method); } /** *

Returns a stub builder for {@link MethodType#CLIENT_STREAMING} method or {@link * MethodType#BIDI_STREAMING} method with a single response at request stream completion. * * @deprecated Not yet implemented */ @Deprecated public static ClientStreamingMethodStubBuilderStep clientStreamingMethod( @Nonnull MethodDescriptor method) { throw new GrpcMockException("Not yet implemented"); } /** *

Returns a stub builder for {@link MethodType#BIDI_STREAMING} method. * * @deprecated Not yet implemented */ @Deprecated public static BidiStreamingMethodStubBuilderStep bidiStreamingMethod( @Nonnull MethodDescriptor method) { throw new GrpcMockException("Not yet implemented"); } /** * Returns a response action, which will send out the given response object via {@link * StreamObserver#onNext}. */ public static ObjectResponseActionBuilder response( @Nonnull RespT responseObject) { return new ObjectResponseActionBuilderImpl<>(responseObject); } /** *

Returns a response action, which will send out * the given exception via {@link StreamObserver#onError}. *

It not recommended to use this methods, because without * a proper {@link ServerInterceptor} translating non-gRPC exceptions to gRPC ones It will be * translated to {@link Status#UNKNOWN} type of exception without any message. The {@link * #statusException(Status)} should be used to define concrete gRPC errors. */ public static ExceptionResponseActionBuilder exception(@Nonnull Throwable exception) { return new ExceptionResponseActionBuilderImpl(exception); } /** *

Returns a response action, which will send out a {@link StatusRuntimeException} * with given {@link Status} via {@link StreamObserver#onError}. */ public static ExceptionResponseActionBuilder statusException(@Nonnull Status status) { return new ExceptionResponseActionBuilderImpl(status); } /** *

Returns a stream response, which can respond with multiple {@link ResponseAction}. */ public static ObjectStreamResponseBuilderStep stream( @Nonnull ObjectResponseActionBuilder responseAction ) { Objects.requireNonNull(responseAction); return new StreamResponseBuilderImpl<>(responseAction.build()); } /** *

Returns a stream response, which can respond with multiple {@link ResponseAction}. *

In order to configure a {@link Delay} for the actions see {@link GrpcMock#response} method. * * @param responses single response objects for the stream response. Will be returned in provided * list order. */ public static ObjectStreamResponseBuilderStep stream( @Nonnull List responses ) { Objects.requireNonNull(responses); responses.forEach(Objects::requireNonNull); return new StreamResponseBuilderImpl<>(responses.stream() .map(GrpcMock::response) .map(ObjectResponseActionBuilder::build) .collect(Collectors.toList())); } /** *

Returns a stream response, which can respond with multiple {@link ResponseAction}. *

In order to configure a {@link Delay} for the actions see {@link GrpcMock#response} method. * * @param responses single response objects for the stream response. Will be returned in provided * array order. */ public static ObjectStreamResponseBuilderStep stream(@Nonnull RespT... responses) { return stream(Arrays.asList(responses)); } /** *

Returns a terminating stream response, which will respond with {@link ResponseAction} and * terminate the call, since it will be {@link StreamObserver#onError} response. */ public static ExceptionStreamResponseBuildersStep stream( @Nonnull ExceptionResponseActionBuilder responseAction ) { Objects.requireNonNull(responseAction); return new StreamResponseBuilderImpl<>(responseAction.build()); } /** *

Verify that given method was called exactly once. *

This is the same as invoking verifyThat(method, times(1)). * * @throws GrpcMockVerificationError if the verify step fails. */ public static void verifyThat(@Nonnull MethodDescriptor method) { verifyThat(calledMethod(method), times(1)); } /** *

Verify that given {@link RequestPattern} was called exactly once. *

This is the same as invoking verifyThat(requestPattern, times(1)). * * @throws GrpcMockVerificationError if the verify step fails. */ public static void verifyThat(@Nonnull RequestPatternBuilderStep requestPattern) { verifyThat(requestPattern, times(1)); } /** *

Verify that given method was called number of times satisfying provided {@link * CountMatcher}. * * @throws GrpcMockVerificationError if the verify step fails. */ public static void verifyThat( @Nonnull MethodDescriptor method, @Nonnull CountMatcher countMatcher ) { verifyThat(calledMethod(method), countMatcher); } /** *

Verify that given {@link RequestPattern} was called number of times satisfying provided * {@link CountMatcher}. * * @throws GrpcMockVerificationError if the verify step fails. */ public static void verifyThat( @Nonnull RequestPatternBuilderStep requestPattern, @Nonnull CountMatcher countMatcher ) { Objects.requireNonNull(requestPattern); INSTANCE.get().verifyThat(requestPattern.build(), countMatcher); } /** * Returns request pattern builder instance, used for verifying call count using {@link * #verifyThat(RequestPatternBuilderStep, CountMatcher)}. */ public static RequestPatternBuilderStep calledMethod( @Nonnull MethodDescriptor method ) { return new RequestPatternBuilderImpl<>(method); } /** * Called exactly the specified number of times. */ public static CountMatcher times(int count) { return CountMatcher.times(count); } /** * Never called. */ public static CountMatcher never() { return CountMatcher.never(); } /** * Called the specified number of times or more. */ public static CountMatcher atLeast(int count) { return CountMatcher.atLeast(count); } /** * Called the specified number of times or less. */ public static CountMatcher atMost(int count) { return CountMatcher.atMost(count); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy