From e33a6a179832c03dec079a398193eef4ba2eac7f Mon Sep 17 00:00:00 2001 From: Martin Ocenas Date: Wed, 8 Nov 2023 13:58:04 +0100 Subject: [PATCH] Add test coverage for issue 36402 for branch 3.2 --- .../quarkus/ts/http/advanced/SseResource.java | 77 +++++++++++++++++++ .../src/main/resources/application.properties | 2 + .../ts/http/advanced/BaseHttpAdvancedIT.java | 13 ++++ 3 files changed, 92 insertions(+) create mode 100644 http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/SseResource.java diff --git a/http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/SseResource.java b/http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/SseResource.java new file mode 100644 index 0000000000..79b327a455 --- /dev/null +++ b/http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/SseResource.java @@ -0,0 +1,77 @@ +package io.quarkus.ts.http.advanced; + +import java.util.Arrays; +import java.util.concurrent.locks.LockSupport; + +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.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 SseResource { + @Context + Sse sse; + + @GET + @Path("/client") + public String sseClient() { + try { + return consumeSse(); + } + // in case that https://github.com/quarkusio/quarkus/issues/36402 throws java.lang.RuntimeException: java.lang.ClassNotFoundException: + // catch it and return the error message + catch (RuntimeException exception) { + return exception.getMessage(); + } + } + + private String consumeSse() { + StringBuilder response = new StringBuilder(); + int port = ConfigProvider.getConfig().getValue("quarkus.http.port", Integer.class); + + /* + * Client connects to itself (to server endpoint running on same app), + * because for https://github.com/quarkusio/quarkus/issues/36402 to reproduce client must run on native app. + * Which cannot be done in test code itself. + * This method acts just as a client + */ + WebTarget target = ClientBuilder.newClient().target("http://localhost:" + port + "/api/sse/server"); + SseEventSource updateSource = SseEventSource.target(target).build(); + updateSource.register(ev -> { + response.append("event: ").append(ev.getName()).append(" ").append(ev.readData()); + response.append("\n"); + + }, thr -> { + response.append("Error in SSE, message: ").append(thr.getMessage()).append("\n"); + response.append(Arrays.toString(thr.getStackTrace())); + }); + updateSource.open(); + + LockSupport.parkNanos(1_000_000_000L); + return response.toString(); + } + + @GET + @Path("/server") + @Produces(MediaType.SERVER_SENT_EVENTS) + public void sendSseEvents(@Context SseEventSink eventSink) { + eventSink.send(createEvent("test234", "test")); + } + + private OutboundSseEvent createEvent(String name, String data) { + return sse.newEventBuilder() + .name(name) + .data(data) + .build(); + } +} diff --git a/http/http-advanced/src/main/resources/application.properties b/http/http-advanced/src/main/resources/application.properties index 904607fef3..8c28597093 100644 --- a/http/http-advanced/src/main/resources/application.properties +++ b/http/http-advanced/src/main/resources/application.properties @@ -45,6 +45,8 @@ quarkus.keycloak.policy-enforcer.paths.version.enforcement-mode=DISABLED # Application endpoints quarkus.keycloak.policy-enforcer.paths.hello.path=/api/hello/* quarkus.keycloak.policy-enforcer.paths.hello.enforcement-mode=DISABLED +quarkus.keycloak.policy-enforcer.paths.sse.path=/api/sse/* +quarkus.keycloak.policy-enforcer.paths.sse.enforcement-mode=DISABLED quarkus.keycloak.policy-enforcer.paths.details.path=/api/details/* quarkus.keycloak.policy-enforcer.paths.details.enforcement-mode=DISABLED quarkus.keycloak.policy-enforcer.paths.grpc.path=/api/grpc/* diff --git a/http/http-advanced/src/test/java/io/quarkus/ts/http/advanced/BaseHttpAdvancedIT.java b/http/http-advanced/src/test/java/io/quarkus/ts/http/advanced/BaseHttpAdvancedIT.java index 0f943a88d2..994232ebab 100644 --- a/http/http-advanced/src/test/java/io/quarkus/ts/http/advanced/BaseHttpAdvancedIT.java +++ b/http/http-advanced/src/test/java/io/quarkus/ts/http/advanced/BaseHttpAdvancedIT.java @@ -8,6 +8,8 @@ import static org.hamcrest.Matchers.in; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.net.URISyntaxException; @@ -60,6 +62,7 @@ public abstract class BaseHttpAdvancedIT { private static final String PASSWORD = "password"; private static final String KEY_STORE_PATH = "META-INF/resources/server.keystore"; private static final int ASSERT_TIMEOUT_SECONDS = 10; + private static final String SSE_ERROR_MESSAGE = "java.lang.ClassNotFoundException: Provider for jakarta.ws.rs.sse.SseEventSource.Builder cannot be found"; protected abstract RestService getApp(); @@ -242,6 +245,16 @@ public void keepRequestScopeValuesAfterEventPropagation() { "Unexpected requestScope custom context value"); } + @Test + @Tag("https://github.com/quarkusio/quarkus/issues/36402") + public void sseConnectionTest() { + String response = getApp().given().get("/api/sse/client").thenReturn().body().asString(); + + assertFalse(response.contains(SSE_ERROR_MESSAGE), + "SSE failed, https://github.com/quarkusio/quarkus/issues/36402 not fixed"); + assertTrue(response.contains("event: test234 test"), "SSE failed, unknown bug. Response: " + response); + } + protected Protocol getProtocol() { return Protocol.HTTPS; }