Skip to content

Commit

Permalink
feat: add backpressure tests, use assetj
Browse files Browse the repository at this point in the history
Signed-off-by: Akshay Patidar <akshaypatidar1999@gmail.com>
  • Loading branch information
akshaypatidar1999 committed Nov 30, 2023
1 parent 8f537eb commit 6c26ca0
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 113 deletions.
23 changes: 16 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,11 @@
<logback.version>1.4.12</logback.version>
<commons-io.version>2.11.0</commons-io.version>
<checkstyle.version>8.32</checkstyle.version>

<!-- Tests <-->
<junit-jupiter.version>5.7.0</junit-jupiter.version>
<junit.version>4.13.2</junit.version>
<hamcrest.version>2.2</hamcrest.version>
<assertj.version>3.24.2</assertj.version>

<maven.compiler.plugin.version>3.8.1</maven.compiler.plugin.version>
<maven.compile.version>11</maven.compile.version>
Expand Down Expand Up @@ -217,16 +219,23 @@
</dependency>

<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>${hamcrest.version}</version>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>${guice.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>${guice.version}</version>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-client</artifactId>
<version>${vertx.version}</version>
<scope>test</scope>
</dependency>

Expand Down
5 changes: 4 additions & 1 deletion src/main/java/com/dream11/rest/AbstractRestVerticle.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import io.reactivex.rxjava3.core.Single;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.rxjava3.core.AbstractVerticle;
import io.vertx.rxjava3.core.Context;
import io.vertx.rxjava3.core.RxHelper;
import io.vertx.rxjava3.core.http.HttpServer;
import io.vertx.rxjava3.core.http.HttpServerRequest;
import io.vertx.rxjava3.ext.web.Router;
Expand Down Expand Up @@ -69,8 +71,9 @@ private Single<HttpServer> startHttpServer() {
.map(HttpServerRequest::pause)
.onBackpressureDrop(req -> {
log.error("Dropping request with status 503");
req.response().setStatusCode(503).end();
req.getDelegate().response().setStatusCode(503).end();
})
.observeOn(RxHelper.scheduler(new Context(this.context))) // For backpressure
.doOnNext(req -> {
if (req.path().matches("/swagger(.*)")) {
router.handle(req);
Expand Down
165 changes: 101 additions & 64 deletions src/test/java/com/dream11/rest/RestApiIT.java
Original file line number Diff line number Diff line change
@@ -1,124 +1,161 @@
package com.dream11.rest;


import static org.assertj.core.api.Assertions.assertThat;

import io.reactivex.rxjava3.core.Single;
import io.vertx.core.json.JsonObject;
import io.vertx.junit5.VertxExtension;
import io.vertx.rxjava3.core.MultiMap;
import io.vertx.rxjava3.core.Vertx;
import io.vertx.rxjava3.core.buffer.Buffer;
import io.vertx.rxjava3.ext.web.client.HttpResponse;
import io.vertx.rxjava3.ext.web.client.WebClient;
import java.util.ArrayList;
import java.util.List;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

@ExtendWith({VertxExtension.class, Setup.class})
@Slf4j
class RestApiIT {

final CloseableHttpClient httpClient = HttpClients.createDefault();
final WebClient webClient = WebClient.create(Vertx.vertx());

@Test
void nullHeaderTest() throws IOException {
void nullHeaderTest() {
// arrange
String uri = String.format("http://127.0.0.1:%s%s/1?testFilter=query&double=1.1&float=1.1&integer=1&long=1",
Constants.APPLICATION_PORT, Constants.VALIDATION_ROUTE_PATH);
String path = String.format("%s/1?testFilter=query&double=1.1&float=1.1&integer=1&long=1", Constants.VALIDATION_ROUTE_PATH);
// act
HttpResponse response = httpClient.execute(new HttpGet(uri));
int statusCode = this.makeGetRequest(path)
.map(HttpResponse::statusCode)
.blockingGet();
// assert
assertThat(response.getStatusLine().getStatusCode(), equalTo(400));
assertThat(statusCode).isEqualTo(400);
}

@Test
void nullQueryParamTest() throws IOException {
void nullQueryParamTest() {
// arrange
String uri = String.format("http://127.0.0.1:%s%s/1", Constants.APPLICATION_PORT, Constants.VALIDATION_ROUTE_PATH);
HttpGet request = new HttpGet(uri);
request.setHeader("testHeader", "1");
String path = String.format("%s/1", Constants.VALIDATION_ROUTE_PATH);
MultiMap headers = MultiMap.caseInsensitiveMultiMap().add("testHeader", "1");
// act
HttpResponse response = httpClient.execute(request);
int statusCode = this.makeGetRequest(path, headers)
.map(HttpResponse::statusCode)
.blockingGet();
// assert
assertThat(response.getStatusLine().getStatusCode(), equalTo(400));
assertThat(statusCode).isEqualTo(400);
}

@ParameterizedTest
@CsvSource({"/class", "/method"})
void timeoutAnnotationTest(String path) throws IOException {
void timeoutAnnotationTest(String param) {
// arrange
String uri = String.format("http://127.0.0.1:%s%s%s", Constants.APPLICATION_PORT, Constants.TIMEOUT_ROUTE_PATH, path);
String path = String.format("%s%s", Constants.TIMEOUT_ROUTE_PATH, param);
// act
HttpResponse response = httpClient.execute(new HttpGet(uri));
int statusCode = this.makeGetRequest(path)
.map(HttpResponse::statusCode)
.blockingGet();
// assert
assertThat(response.getStatusLine().getStatusCode(), equalTo(503));
assertThat(statusCode).isEqualTo(503);
}

@ParameterizedTest
@CsvSource({"integer", "long", "float", "double"})
void typeTypeValidationParamTest(String param) throws IOException {
void typeTypeValidationParamTest(String param) {
// arrange
String path = String.format("%s/1?testFilter=query&%s=param", Constants.VALIDATION_ROUTE_PATH, param);
MultiMap headers = MultiMap.caseInsensitiveMultiMap().add("testHeader", "1");
// act
HttpResponse<Buffer> response = this.makeGetRequest(path, headers)
.blockingGet();
JsonObject responseBody = response.bodyAsJsonObject();
// assert
assertThat(response.statusCode()).isEqualTo(400);
assertThat(responseBody.getJsonObject("error").getString("code")).isEqualTo("BAD_REQUEST");
assertThat(responseBody.getJsonObject("error").getString("message")).isEqualTo(
String.format("Query param '%s' must be %s", param, param));
}

@Test
void validateBodyTest() {
// arrange
String uri =
String.format("http://127.0.0.1:%s%s/1?testFilter=query&%s=param", Constants.APPLICATION_PORT, Constants.VALIDATION_ROUTE_PATH,
param);
HttpGet request = new HttpGet(uri);
request.setHeader("testHeader", "1");
String path = String.format("%s", Constants.VALIDATION_ROUTE_PATH);
JsonObject body = new JsonObject().put("resourceId", "Hello");
// act
HttpResponse response = httpClient.execute(request);
JsonObject responseBody = new JsonObject(IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8));
HttpResponse<Buffer> response = this.makePostRequest(path, body)
.blockingGet();
JsonObject responseBody = response.bodyAsJsonObject();
// assert
assertThat(response.getStatusLine().getStatusCode(), equalTo(400));
assertThat(responseBody.getJsonObject("error").getString("code"), equalTo("BAD_REQUEST"));
assertThat(responseBody.getJsonObject("error").getString("message"),
equalTo(String.format("Query param '%s' must be %s", param, param)));
assertThat(response.statusCode()).isEqualTo(400);
assertThat(responseBody.getJsonObject("error").getString("code")).isEqualTo("BAD_REQUEST");
assertThat(responseBody.getJsonObject("error").getString("message")).isEqualTo("resourceId must be integer");
}

@Test
void validateBodyTest() throws IOException {
void positiveBodyTest() {
// arrange
String uri = String.format("http://127.0.0.1:%s%s", Constants.APPLICATION_PORT, Constants.VALIDATION_ROUTE_PATH);
HttpPost request = new HttpPost(uri);
request.setHeader("Content-type", "application/json");
JsonObject json = new JsonObject().put("resourceId", "Hello");
request.setEntity(new StringEntity(json.toString()));
String path = String.format("%s", Constants.VALIDATION_ROUTE_PATH);
JsonObject body = new JsonObject().put("resourceId", "1");
// act
HttpResponse response = httpClient.execute(request);
JsonObject responseBody = new JsonObject(IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8));
HttpResponse<Buffer> response = this.makePostRequest(path, body)
.blockingGet();
// assert
assertThat(response.getStatusLine().getStatusCode(), equalTo(400));
assertThat(responseBody.getJsonObject("error").getString("code"), equalTo("BAD_REQUEST"));
assertThat(responseBody.getJsonObject("error").getString("message"), equalTo("resourceId must be integer"));
assertThat(response.statusCode()).isEqualTo(200);
}

@Test
void positiveBodyTest() throws IOException {
void routeNotFoundTest() {
// arrange
String uri = String.format("http://127.0.0.1:%s%s", Constants.APPLICATION_PORT, Constants.VALIDATION_ROUTE_PATH);
HttpPost request = new HttpPost(uri);
request.setHeader("Content-type", "application/json");
JsonObject json = new JsonObject().put("resourceId", "1");
request.setEntity(new StringEntity(json.toString()));
String path = "/nonexistent";
// act
HttpResponse response = httpClient.execute(request);
int statusCode = this.makeGetRequest(path)
.map(HttpResponse::statusCode)
.blockingGet();
// assert
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
assertThat(statusCode).isEqualTo(404);
}


@Test
void routeNotFoundTest() throws IOException {
@SneakyThrows
void testBackPressure() {
// arrange
String uri = String.format("http://127.0.0.1:%s%s", Constants.APPLICATION_PORT, "/nonexistent");
JsonObject body = JsonObject.of("resourceId", "1");
List<Single<HttpResponse<Buffer>>> responseSingles = new ArrayList<>();
for (int i = 0; i < 10; i++) {
responseSingles.add(this.makePostRequest(Constants.VALIDATION_ROUTE_PATH, body));
}

// act
HttpResponse response = httpClient.execute(new HttpGet(uri));
List<Integer> statusCodes = Single.merge(responseSingles)
.map(HttpResponse::statusCode)
.toList()
.blockingGet();

// assert
assertThat(response.getStatusLine().getStatusCode(), equalTo(404));
assertThat(statusCodes).contains(200, 503);
}

private Single<HttpResponse<Buffer>> makePostRequest(String path, JsonObject body) {
String uri = String.format("http://127.0.0.1:%s%s", Constants.APPLICATION_PORT, path);
return this.webClient.postAbs(uri)
.putHeader("Content-type", "application/json")
.rxSendJsonObject(body);
}

private Single<HttpResponse<Buffer>> makeGetRequest(String path, MultiMap headers) {
String uri = String.format("http://127.0.0.1:%s%s", Constants.APPLICATION_PORT, path);
return this.webClient.getAbs(uri)
.putHeaders(headers)
.rxSend();
}

private Single<HttpResponse<Buffer>> makeGetRequest(String path) {
return makeGetRequest(path, MultiMap.caseInsensitiveMultiMap());
}
}
4 changes: 3 additions & 1 deletion src/test/java/com/dream11/rest/Setup.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.dream11.rest.util.SharedDataUtil;
import com.dream11.rest.verticle.RestVerticle;
import com.google.inject.Guice;
import io.vertx.core.DeploymentOptions;
import io.vertx.rxjava3.core.Vertx;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.extension.AfterAllCallback;
Expand All @@ -24,7 +25,8 @@ public void beforeAll(ExtensionContext extensionContext) {
GuiceInjector injector = new GuiceInjector(Guice.createInjector());
SharedDataUtil.setInstance(vertx.getDelegate(), injector);
final String verticleName = RestVerticle.class.getName();
String deploymentId = this.vertx.rxDeployVerticle(injector.getInstance(RestVerticle.class))
System.setProperty("rx3.buffer-size", "1"); // Set rxjava buffer size
String __ = this.vertx.rxDeployVerticle(injector.getInstance(RestVerticle.class), new DeploymentOptions().setInstances(1))
.doOnError(error -> log.error("Error in deploying verticle : {}", verticleName, error))
.doOnSuccess(v -> log.info("Deployed verticle : {}", verticleName)).blockingGet();
}
Expand Down
18 changes: 9 additions & 9 deletions src/test/java/com/dream11/rest/exception/RestExceptionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import org.junit.jupiter.api.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.assertj.core.api.Assertions.assertThat;


class RestExceptionTest {
@Test
Expand All @@ -17,13 +17,13 @@ void testRestException() {
RestException restException = new RestException(restError);
RestException restExceptionWithCause = new RestException(restError, cause);
// assert
assertThat(restException.getMessage(), equalTo(restError.getErrorMessage()));
assertThat(restException.getHttpStatusCode(), equalTo(restError.getHttpStatusCode()));
assertThat(restException.getErrorCode(), equalTo(restError.getErrorCode()));
assertThat(restException.getMessage()).isEqualTo(restError.getErrorMessage());
assertThat(restException.getHttpStatusCode()).isEqualTo(restError.getHttpStatusCode());
assertThat(restException.getErrorCode()).isEqualTo(restError.getErrorCode());

assertThat(restExceptionWithCause.getMessage(), equalTo(restError.getErrorMessage()));
assertThat(restExceptionWithCause.getHttpStatusCode(), equalTo(restError.getHttpStatusCode()));
assertThat(restExceptionWithCause.getErrorCode(), equalTo(restError.getErrorCode()));
assertThat(restExceptionWithCause.getCause(), equalTo(cause));
assertThat(restExceptionWithCause.getMessage()).isEqualTo(restError.getErrorMessage());
assertThat(restExceptionWithCause.getHttpStatusCode()).isEqualTo(restError.getHttpStatusCode());
assertThat(restExceptionWithCause.getErrorCode()).isEqualTo(restError.getErrorCode());
assertThat(restExceptionWithCause.getCause()).isEqualTo(cause);
}
}
14 changes: 7 additions & 7 deletions src/test/java/com/dream11/rest/util/AnnotationUtilTest.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package com.dream11.rest.util;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;

import com.dream11.rest.Constants;
import com.dream11.rest.route.TimeoutRoute;
import com.dream11.rest.route.ValidationCheckRoute;
import jakarta.ws.rs.Path;
import org.junit.jupiter.api.Test;

import java.lang.annotation.Annotation;
import java.util.List;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

class AnnotationUtilTest {
@Test
Expand All @@ -20,8 +19,9 @@ void testAnnotatedClasses() {
// act
List<Class<?>> classes = AnnotationUtil.getClassesWithAnnotation(Constants.TEST_PACKAGE_NAME, annotation);
// assert
assertThat(classes.size(), equalTo(2));
assertThat(classes, containsInAnyOrder(ValidationCheckRoute.class, TimeoutRoute.class));
assertThat(classes)
.hasSize(2)
.contains(ValidationCheckRoute.class, TimeoutRoute.class);

}
}
Loading

0 comments on commit 6c26ca0

Please sign in to comment.