Skip to content

Commit

Permalink
Added support for fixed and random delays in the DSL
Browse files Browse the repository at this point in the history
  • Loading branch information
tomakehurst committed Dec 8, 2023
1 parent 8a689bf commit aa5f777
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
package org.wiremock.grpc.dsl;

import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
import com.github.tomakehurst.wiremock.http.Fault;
import com.github.tomakehurst.wiremock.http.*;
import org.wiremock.annotations.Beta;

@Beta(justification = "Incubating extension: https://github.com/wiremock/wiremock/issues/2383")
Expand All @@ -33,6 +33,8 @@ public class GrpcResponseDefinitionBuilder {

private boolean templatingEnabled;

private DelayDistribution delay;

public GrpcResponseDefinitionBuilder(WireMockGrpc.Status grpcStatus) {
this(grpcStatus, null);
}
Expand All @@ -59,6 +61,26 @@ public GrpcResponseDefinitionBuilder withTemplatingEnabled(boolean enabled) {
return this;
}

public GrpcResponseDefinitionBuilder withFixedDelay(long milliseconds) {
this.delay = new FixedDelayDistribution(milliseconds);
return null;
}

public GrpcResponseDefinitionBuilder withRandomDelay(DelayDistribution distribution) {
this.delay = distribution;
return this;
}

public GrpcResponseDefinitionBuilder withLogNormalRandomDelay(
double medianMilliseconds, double sigma) {
return withRandomDelay(new LogNormal(medianMilliseconds, sigma));
}

public GrpcResponseDefinitionBuilder withUniformRandomDelay(
int lowerMilliseconds, int upperMilliseconds) {
return withRandomDelay(new UniformDistribution(lowerMilliseconds, upperMilliseconds));
}

public ResponseDefinitionBuilder build() {
if (fault != null) {
return ResponseDefinitionBuilder.responseDefinition().withFault(fault);
Expand All @@ -76,6 +98,10 @@ public ResponseDefinitionBuilder build() {
responseDefinitionBuilder.withTransformers("response-template");
}

if (delay != null) {
responseDefinitionBuilder.withRandomDelay(delay);
}

return responseDefinitionBuilder.withBody(json);
}
}
22 changes: 22 additions & 0 deletions src/main/java/org/wiremock/grpc/dsl/GrpcStubMappingBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@

import com.github.tomakehurst.wiremock.client.MappingBuilder;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.http.DelayDistribution;
import com.github.tomakehurst.wiremock.http.Fault;
import com.github.tomakehurst.wiremock.http.LogNormal;
import com.github.tomakehurst.wiremock.http.UniformDistribution;
import com.github.tomakehurst.wiremock.matching.StringValuePattern;
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
import java.util.ArrayList;
Expand Down Expand Up @@ -58,6 +61,25 @@ public GrpcStubMappingBuilder willReturn(Fault fault) {
return this;
}

public GrpcStubMappingBuilder withFixedDelay(long milliseconds) {
responseBuilder.withFixedDelay(milliseconds);
return this;
}

public GrpcStubMappingBuilder withDelay(DelayDistribution delay) {
responseBuilder.withRandomDelay(delay);
return this;
}

public GrpcStubMappingBuilder withLogNormalRandomDelay(double medianMilliseconds, double sigma) {
return withDelay(new LogNormal(medianMilliseconds, sigma));
}

public GrpcStubMappingBuilder withUniformRandomDelay(
int lowerMilliseconds, int upperMilliseconds) {
return withDelay(new UniformDistribution(lowerMilliseconds, upperMilliseconds));
}

public StubMapping build(String serviceName) {
final MappingBuilder mappingBuilder = WireMock.post(grpcUrlPath(serviceName, method));
requestMessageJsonPatterns.forEach(mappingBuilder::withRequestBody);
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/org/wiremock/grpc/internal/Delays.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2023 Thomas Akehurst
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wiremock.grpc.internal;

import com.github.tomakehurst.wiremock.http.Response;
import java.util.concurrent.TimeUnit;

public class Delays {

public static void delayIfRequired(Response response) {
final long delayMillis = response.getInitialDelay();
if (delayMillis > 0) {
try {
TimeUnit.MILLISECONDS.sleep(delayMillis);
} catch (InterruptedException var4) {
Thread.currentThread().interrupt();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import static org.wiremock.grpc.dsl.GrpcResponseDefinitionBuilder.GRPC_STATUS_NAME;
import static org.wiremock.grpc.dsl.GrpcResponseDefinitionBuilder.GRPC_STATUS_REASON;
import static org.wiremock.grpc.internal.Delays.delayIfRequired;

import com.github.tomakehurst.wiremock.http.HttpHeader;
import com.github.tomakehurst.wiremock.http.StubRequestHandler;
Expand Down Expand Up @@ -62,6 +63,8 @@ public void invoke(DynamicMessage request, StreamObserver<DynamicMessage> respon
(req, resp, attributes) -> {
final HttpHeader statusHeader = resp.getHeaders().getHeader(GRPC_STATUS_NAME);

delayIfRequired(resp);

if (!statusHeader.isPresent() && resp.getStatus() == 404) {
responseObserver.onError(
Status.NOT_FOUND
Expand Down
32 changes: 32 additions & 0 deletions src/test/java/org/wiremock/grpc/GrpcAcceptanceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.assertThrows;
Expand All @@ -28,6 +29,7 @@
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.http.Fault;
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
import com.google.common.base.Stopwatch;
import com.google.protobuf.Empty;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
Expand Down Expand Up @@ -254,4 +256,34 @@ void networkFault() {
assertThrows(StatusRuntimeException.class, () -> greetingsClient.greet("Alan"));
assertThat(exception.getMessage(), is("UNKNOWN"));
}

@Test
void fixedDelay() {
mockGreetingService.stubFor(
method("greeting")
.willReturn(json("{ \"greeting\": \"Delayed hello\" }"))
.withFixedDelay(1000));

Stopwatch stopwatch = Stopwatch.createStarted();
String greeting = greetingsClient.greet("Tom");
stopwatch.stop();

assertThat(greeting, is("Delayed hello"));
assertThat(stopwatch.elapsed(MILLISECONDS), greaterThanOrEqualTo(1000L));
}

@Test
void randomDelay() {
mockGreetingService.stubFor(
method("greeting")
.willReturn(json("{ \"greeting\": \"Delayed hello\" }"))
.withUniformRandomDelay(500, 1000));

Stopwatch stopwatch = Stopwatch.createStarted();
String greeting = greetingsClient.greet("Tom");
stopwatch.stop();

assertThat(greeting, is("Delayed hello"));
assertThat(stopwatch.elapsed(MILLISECONDS), greaterThanOrEqualTo(500L));
}
}

0 comments on commit aa5f777

Please sign in to comment.