Skip to content

Commit

Permalink
implementing vertx-web-validation module tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jcarranzan committed Nov 15, 2023
1 parent 168efff commit b404307
Show file tree
Hide file tree
Showing 71 changed files with 2,828 additions and 148 deletions.
41 changes: 15 additions & 26 deletions .github/mvn-settings.xml
Original file line number Diff line number Diff line change
@@ -1,30 +1,18 @@
<settings>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
</servers>
<profiles>
<profile>
<id>google-mirror-jboss-proxy</id>
<id>google-mirror</id>
<repositories>
<repository>
<id>google-maven-central</id>
<name>GCS Maven Central mirror EU</name>
<url>https://maven-central-eu.storage-download.googleapis.com/repos/central/data/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>jboss-maven-central-proxy</id>
<name>JBoss Maven Central proxy</name>
<url>https://repository.jboss.org/nexus/content/repositories/central/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>gradle-official-repository</id>
<name>Gradle Official Repository</name>
<url>https://repo.gradle.org/gradle/libs-releases-local/</url>
<url>https://maven-central.storage-download.googleapis.com/maven2/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
Expand All @@ -34,17 +22,18 @@
<pluginRepository>
<id>google-maven-central</id>
<name>GCS Maven Central mirror EU</name>
<url>https://maven-central-eu.storage-download.googleapis.com/repos/central/data/</url>
</pluginRepository>
<pluginRepository>
<id>jboss-maven-central-proxy</id>
<name>JBoss Maven Central proxy</name>
<url>https://repository.jboss.org/nexus/content/repositories/central/</url>
<url>https://maven-central.storage-download.googleapis.com/maven2/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>google-mirror-jboss-proxy</activeProfile>
<activeProfile>google-mirror</activeProfile>
</activeProfiles>
</settings>
191 changes: 110 additions & 81 deletions README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public class AnalyticsUtils {
"quarkus-smallrye-jwt-build",
"quarkus-smallrye-metrics",
"quarkus-smallrye-openapi",
"quarkus-smallrye-opentracing",
"quarkus-opentelemetry",
"quarkus-smallrye-reactive-messaging",
"quarkus-smallrye-reactive-messaging-kafka",
"quarkus-spring-cache",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.quarkus.ts.http.advanced.reactive;

import java.util.Map;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.CookieParam;
import jakarta.ws.rs.FormParam;
Expand Down Expand Up @@ -56,6 +58,19 @@ public Response getSameSiteAttributeFromFormParam(@FormParam(TEST_COOKIE) String
return responseBuilder.cookie(newCookie).build();
}

@GET
@Path("newcookie-serialization")
public Map<String, NewCookie> getRequestCookies(HttpHeaders httpHeaders) {
NewCookie cookie = new NewCookie.Builder(TEST_COOKIE).value("test-cookie-value").build();
return Map.of(cookie.getName(), cookie);
}

@GET
@Path("cookie-serialization")
public Map<String, jakarta.ws.rs.core.Cookie> test(HttpHeaders httpHeaders) {
return httpHeaders.getCookies();
}

public static String toRawCookie(String sameSite) {
if (sameSite == null || sameSite.isEmpty()) {
return String.format("%s=\"test-cookie-value\";Version=\"1\";", TEST_COOKIE);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package io.quarkus.ts.http.advanced.reactive;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import jakarta.ws.rs.ConstrainedTo;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NameBinding;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.RuntimeType;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.ext.MessageBodyWriter;
import jakarta.ws.rs.ext.Provider;
import jakarta.ws.rs.ext.WriterInterceptor;
import jakarta.ws.rs.ext.WriterInterceptorContext;

@Path("/intercepted")
public class InterceptedResource {

/**
* Interceptors write their message to this list, when they are invoked
* It is a bit dumb way, but it is the easier to get indicators if interceptors were invoked to the client
*/
public static List<String> interceptorMessages = new ArrayList<>();

@WithWriterInterceptor
@GET
public InterceptedString getInterceptedString() {
return new InterceptedString("foo");
}

@GET()
@Path("/messages")
@Produces(MediaType.TEXT_PLAIN)
public String getMessages() {
StringBuilder outputMessage = new StringBuilder();
for (String string : interceptorMessages) {
outputMessage.append(string);
}
return outputMessage.toString();
}

public static class InterceptedString {
public String name;

public InterceptedString(String name) {
this.name = name;
}
}

/**
* This annotation binds the providers to only intercept the method in this class.
* Otherwise, they would be global and intercept all endpoints across the entire application.
*/
@NameBinding
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface WithWriterInterceptor {

}

@Provider
public static class InterceptedStringHandler implements MessageBodyWriter<InterceptedString> {
@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return type == InterceptedString.class;
}

@Override
public void writeTo(InterceptedString interceptedString, Class<?> type, Type genericType, Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
throws IOException, WebApplicationException {
entityStream.write((interceptedString.name).getBytes(StandardCharsets.UTF_8));
}
}

@Provider
@WithWriterInterceptor
public static class UnconstrainedWriterInterceptor implements WriterInterceptor {
@Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
InterceptedResource.interceptorMessages.add("Unconstrained interceptor ");
context.proceed();
}
}

@Provider
@ConstrainedTo(RuntimeType.CLIENT)
@WithWriterInterceptor
public static class ClientWriterInterceptor implements WriterInterceptor {

@Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
InterceptedResource.interceptorMessages.add("Client interceptor ");
context.proceed();
}
}

@Provider
@ConstrainedTo(RuntimeType.SERVER)
@WithWriterInterceptor
public static class ServerWriterInterceptor implements WriterInterceptor {

@Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
InterceptedResource.interceptorMessages.add("Server interceptor ");
context.proceed();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.quarkus.ts.http.advanced.reactive;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.sse.OutboundSseEvent;
import jakarta.ws.rs.sse.Sse;
import jakarta.ws.rs.sse.SseEventSink;
import jakarta.ws.rs.sse.SseEventSource;

import org.eclipse.microprofile.config.ConfigProvider;

@Path("sse")
public class SseEventUpdateResource {
public static final String DATA_VALUE = "random data value";

@Context
Sse sse;

@GET
@Path("client-update")
@Produces(MediaType.TEXT_PLAIN)
public Response clientUpdate() throws InterruptedException {
String host = ConfigProvider.getConfig().getValue("quarkus.http.host", String.class);
int port = ConfigProvider.getConfig().getValue("quarkus.http.port", Integer.class);
List<String> receivedData = new CopyOnWriteArrayList<>();

WebTarget target = ClientBuilder.newClient().target("http://" + host + ":" + port + "/api/sse/server-update");
try (SseEventSource eventSource = SseEventSource.target(target).build()) {
eventSource.register(ev -> {
String event = "event: name=" + ev.getName() + " data={" + ev.readData() + "} and is empty: " + ev.isEmpty()
+ "\n";
receivedData.add(event);
}, thr -> {
String event = "Error: " + thr.getMessage() + "\n" + Arrays.toString(thr.getStackTrace());
receivedData.add(event);
});

CountDownLatch latch = new CountDownLatch(2);
eventSource.open();
latch.await(1, TimeUnit.SECONDS);
}
return Response.ok(String.join("\n", receivedData)).build();
}

@GET
@Path("server-update")
@Produces(MediaType.SERVER_SENT_EVENTS)
public void updates(@Context SseEventSink eventSink) {
eventSink.send(createEvent("NON EMPTY", DATA_VALUE));
eventSink.send(createEvent("EMPTY", ""));
}

private OutboundSseEvent createEvent(String name, String data) {
return sse.newEventBuilder()
.name(name)
.data(data)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ quarkus.keycloak.policy-enforcer.paths.grpc.path=/api/grpc/*
quarkus.keycloak.policy-enforcer.paths.grpc.enforcement-mode=DISABLED
quarkus.keycloak.policy-enforcer.paths.client.path=/api/client/*
quarkus.keycloak.policy-enforcer.paths.client.enforcement-mode=DISABLED
quarkus.keycloak.policy-enforcer.paths.intercepted.path=/api/intercepted*
quarkus.keycloak.policy-enforcer.paths.intercepted.enforcement-mode=DISABLED
quarkus.keycloak.policy-enforcer.paths.sse.path=/api/sse/*
quarkus.keycloak.policy-enforcer.paths.sse.enforcement-mode=DISABLED
quarkus.oidc.client-id=test-application-client
quarkus.oidc.credentials.secret=test-application-client-secret
# tolerate 1 minute of clock skew between the Keycloak server and the application
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import static io.quarkus.ts.http.advanced.reactive.MultipleResponseSerializersResource.MULTIPLE_RESPONSE_SERIALIZERS_PATH;
import static io.quarkus.ts.http.advanced.reactive.NinetyNineBottlesOfBeerResource.QUARKUS_PLATFORM_VERSION_LESS_THAN_2_8_3;
import static io.quarkus.ts.http.advanced.reactive.NinetyNineBottlesOfBeerResource.QUARKUS_PLATFORM_VERSION_LESS_THAN_2_8_3_VAL;
import static io.quarkus.ts.http.advanced.reactive.SseEventUpdateResource.DATA_VALUE;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import static jakarta.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM;
import static jakarta.ws.rs.core.MediaType.APPLICATION_XML;
Expand Down Expand Up @@ -71,6 +72,7 @@
import io.quarkus.example.StreamingGrpc;
import io.quarkus.test.bootstrap.Protocol;
import io.quarkus.test.bootstrap.RestService;
import io.quarkus.test.scenarios.annotations.DisabledOnNative;
import io.quarkus.test.scenarios.annotations.EnabledOnQuarkusVersion;
import io.quarkus.ts.http.advanced.reactive.clients.HttpVersionClientService;
import io.quarkus.ts.http.advanced.reactive.clients.HttpVersionClientServiceAsync;
Expand Down Expand Up @@ -427,6 +429,35 @@ public void constraintsExist() throws JsonProcessingException {
Assertions.assertEquals("^[A-Za-z]+$", validation.get("pattern").asText());
}

@Test
@Tag("https://github.com/quarkusio/quarkus/pull/36664")
public void interceptedTest() {
// make server to generate a response so interceptors might intercept it
// ignore response, we will read interceptors result later
getApp().given()
.get(ROOT_PATH + "/intercepted")
.thenReturn();

String response = getApp().given()
.get(ROOT_PATH + "/intercepted/messages")
.thenReturn().getBody().asString();

Assertions.assertTrue(response.contains("Unconstrained"), "Unconstrained interceptor should be invoked");
Assertions.assertTrue(response.contains("Server"), "Server interceptor should be invoked");
Assertions.assertFalse(response.contains("Client"), "Client interceptor should not be invoked");
}

@DisplayName("SSE check for event responses values containing empty data")
@Test
@DisabledOnNative(reason = "https://github.com/quarkusio/quarkus/issues/36986")
void testSseResponseForEmptyData() {
getApp().given()
.get(ROOT_PATH + "/sse/client-update")
.then().statusCode(SC_OK)
.body(containsString(String.format("event: name=NON EMPTY data={%s} and is empty: false", DATA_VALUE)),
containsString("event: name=EMPTY data={} and is empty: true"));
}

private void assertAcceptedMediaTypeEqualsResponseBody(String acceptedMediaType) {
getApp()
.given()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package io.quarkus.ts.http.advanced.reactive;

import static io.quarkus.ts.http.advanced.reactive.CookiesResource.TEST_COOKIE;
import static io.restassured.RestAssured.get;
import static io.restassured.RestAssured.given;
import static io.restassured.matcher.RestAssuredMatchers.detailedCookie;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;

import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -52,6 +52,26 @@ void testSameSiteAttributeAddedByVertxHttpExt() {
.cookie("vertx", detailedCookie().sameSite(sameSite).secured(true));
}

@Test
void testNewCookiesSerialization() {
given()
.get("/cookie/newcookie-serialization")
.then()
.statusCode(200)
.body(containsString(String.format("\"name\":\"%s\"", TEST_COOKIE)),
containsString("\"value\":\"test-cookie-value\""));
}

@Test
void testCookiesSerialization() {
given().cookie(String.format("%s=\"test-cookie-value\";", TEST_COOKIE))
.get("/cookie/cookie-serialization")
.then()
.statusCode(200)
.body(containsString(String.format("\"name\":\"%s\"", TEST_COOKIE)),
containsString("\"value\":\"test-cookie-value\""));
}

private static void assertSameSiteAttribute(String sameSite) {
ValidatableResponse response;

Expand Down
Loading

0 comments on commit b404307

Please sign in to comment.