diff --git a/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/BinaryDecodeErrorTest.java b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/BinaryDecodeErrorTest.java new file mode 100644 index 00000000000000..943bde2343b821 --- /dev/null +++ b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/BinaryDecodeErrorTest.java @@ -0,0 +1,63 @@ +package io.quarkus.websockets.next.test.errors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.URI; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.quarkus.websockets.next.BinaryDecodeException; +import io.quarkus.websockets.next.OnBinaryMessage; +import io.quarkus.websockets.next.OnError; +import io.quarkus.websockets.next.WebSocket; +import io.quarkus.websockets.next.WebSocketConnection; +import io.quarkus.websockets.next.test.utils.WSClient; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; +import io.vertx.mutiny.core.Context; + +public class BinaryDecodeErrorTest { + + @RegisterExtension + public static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot(root -> { + root.addClasses(Echo.class, WSClient.class); + }); + + @Inject + Vertx vertx; + + @TestHTTPResource("echo") + URI testUri; + + @Test + void testError() { + WSClient client = WSClient.create(vertx).connect(testUri); + client.send(Buffer.buffer("1")); + client.waitForMessages(1); + assertEquals("Problem decoding: 1", client.getLastMessage().toString()); + } + + @WebSocket(path = "/echo") + public static class Echo { + + @OnBinaryMessage + void process(WebSocketConnection connection, Integer message) { + throw new IllegalStateException(); + } + + @OnError + String decodingError(BinaryDecodeException e) { + assertTrue(Context.isOnWorkerThread()); + return "Problem decoding: " + e.getBytes().toString(); + } + + } + +} diff --git a/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/BinaryEncodeErrorTest.java b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/BinaryEncodeErrorTest.java new file mode 100644 index 00000000000000..7faa33935305a8 --- /dev/null +++ b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/BinaryEncodeErrorTest.java @@ -0,0 +1,63 @@ +package io.quarkus.websockets.next.test.errors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.URI; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.quarkus.websockets.next.BinaryEncodeException; +import io.quarkus.websockets.next.OnBinaryMessage; +import io.quarkus.websockets.next.OnError; +import io.quarkus.websockets.next.WebSocket; +import io.quarkus.websockets.next.WebSocketConnection; +import io.quarkus.websockets.next.test.utils.WSClient; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; +import io.vertx.mutiny.core.Context; + +public class BinaryEncodeErrorTest { + + @RegisterExtension + public static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot(root -> { + root.addClasses(Echo.class, WSClient.class); + }); + + @Inject + Vertx vertx; + + @TestHTTPResource("echo") + URI testUri; + + @Test + void testError() { + WSClient client = WSClient.create(vertx).connect(testUri); + client.send(Buffer.buffer("1")); + client.waitForMessages(1); + assertEquals("Problem encoding: 1", client.getLastMessage().toString()); + } + + @WebSocket(path = "/echo") + public static class Echo { + + @OnBinaryMessage + Integer process(WebSocketConnection connection, Buffer message) { + return Integer.parseInt(message.toString()); + } + + @OnError + String encodingError(BinaryEncodeException e) { + assertTrue(Context.isOnWorkerThread()); + return "Problem encoding: " + e.getEncodedObject().toString(); + } + + } + +} diff --git a/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/GlobalErrorHandlerWithPathParamTest.java b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/GlobalErrorHandlerWithPathParamTest.java new file mode 100644 index 00000000000000..a4ca1f36843177 --- /dev/null +++ b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/GlobalErrorHandlerWithPathParamTest.java @@ -0,0 +1,38 @@ +package io.quarkus.websockets.next.test.errors; + +import static org.junit.jupiter.api.Assertions.fail; + +import jakarta.enterprise.context.ApplicationScoped; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.websockets.next.OnError; +import io.quarkus.websockets.next.PathParam; +import io.quarkus.websockets.next.WebSocketServerException; + +public class GlobalErrorHandlerWithPathParamTest { + + @RegisterExtension + public static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot(root -> { + root.addClasses(GlobalErrorHandlers.class); + }) + .setExpectedException(WebSocketServerException.class); + + @Test + void testMultipleAmbiguousErrorHandlers() { + fail(); + } + + @ApplicationScoped + public static class GlobalErrorHandlers { + + @OnError + void onError(IllegalStateException ise, @PathParam String illegal) { + } + + } + +} diff --git a/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/MultipleAmbiguousErrorHandlersTest.java b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/MultipleAmbiguousErrorHandlersTest.java new file mode 100644 index 00000000000000..cbd863f0babcb6 --- /dev/null +++ b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/MultipleAmbiguousErrorHandlersTest.java @@ -0,0 +1,45 @@ +package io.quarkus.websockets.next.test.errors; + +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.websockets.next.OnError; +import io.quarkus.websockets.next.OnOpen; +import io.quarkus.websockets.next.WebSocket; +import io.quarkus.websockets.next.WebSocketServerException; + +public class MultipleAmbiguousErrorHandlersTest { + + @RegisterExtension + public static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot(root -> { + root.addClasses(Endpoint.class); + }) + .setExpectedException(WebSocketServerException.class); + + @Test + void testMultipleAmbiguousErrorHandlers() { + fail(); + } + + @WebSocket(path = "/end") + public static class Endpoint { + + @OnOpen + void open() { + } + + @OnError + void onError1(IllegalStateException ise) { + } + + @OnError + void onError2(IllegalStateException ise) { + } + + } + +} diff --git a/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/MultipleAmbiguousGlobalErrorHandlersTest.java b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/MultipleAmbiguousGlobalErrorHandlersTest.java new file mode 100644 index 00000000000000..39c39cdff3b2de --- /dev/null +++ b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/MultipleAmbiguousGlobalErrorHandlersTest.java @@ -0,0 +1,41 @@ +package io.quarkus.websockets.next.test.errors; + +import static org.junit.jupiter.api.Assertions.fail; + +import jakarta.enterprise.context.ApplicationScoped; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.websockets.next.OnError; +import io.quarkus.websockets.next.WebSocketServerException; + +public class MultipleAmbiguousGlobalErrorHandlersTest { + + @RegisterExtension + public static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot(root -> { + root.addClasses(GlobalErrorHandlers.class); + }) + .setExpectedException(WebSocketServerException.class); + + @Test + void testMultipleAmbiguousErrorHandlers() { + fail(); + } + + @ApplicationScoped + public static class GlobalErrorHandlers { + + @OnError + void onError1(IllegalStateException ise) { + } + + @OnError + void onError2(IllegalStateException ise) { + } + + } + +} diff --git a/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/RuntimeErrorCloseConnectionTest.java b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/RuntimeErrorCloseConnectionTest.java new file mode 100644 index 00000000000000..b19fc4c9a0597b --- /dev/null +++ b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/RuntimeErrorCloseConnectionTest.java @@ -0,0 +1,59 @@ +package io.quarkus.websockets.next.test.errors; + +import java.net.URI; +import java.time.Duration; + +import jakarta.inject.Inject; + +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.quarkus.websockets.next.OnBinaryMessage; +import io.quarkus.websockets.next.OnError; +import io.quarkus.websockets.next.WebSocket; +import io.quarkus.websockets.next.WebSocketConnection; +import io.quarkus.websockets.next.test.utils.WSClient; +import io.smallrye.mutiny.Uni; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; + +public class RuntimeErrorCloseConnectionTest { + + @RegisterExtension + public static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot(root -> { + root.addClasses(Echo.class, WSClient.class); + }); + + @Inject + Vertx vertx; + + @TestHTTPResource("echo") + URI testUri; + + @Test + void testError() { + WSClient client = WSClient.create(vertx).connect(testUri); + client.sendAndAwait(Buffer.buffer("1")); + Awaitility.await().atMost(Duration.ofSeconds(5)).until(() -> client.isClosed()); + } + + @WebSocket(path = "/echo") + public static class Echo { + + @OnBinaryMessage + void process(Buffer message) { + throw new IllegalStateException("Something went wrong"); + } + + @OnError + Uni runtimeProblem(RuntimeException e, WebSocketConnection connection) { + return connection.close(); + } + + } + +} diff --git a/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/RuntimeErrorTest.java b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/RuntimeErrorTest.java new file mode 100644 index 00000000000000..a519c95ea9be36 --- /dev/null +++ b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/RuntimeErrorTest.java @@ -0,0 +1,117 @@ +package io.quarkus.websockets.next.test.errors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.URI; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import jakarta.annotation.PreDestroy; +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.quarkus.websockets.next.BinaryDecodeException; +import io.quarkus.websockets.next.BinaryEncodeException; +import io.quarkus.websockets.next.OnBinaryMessage; +import io.quarkus.websockets.next.OnError; +import io.quarkus.websockets.next.WebSocket; +import io.quarkus.websockets.next.WebSocketConnection; +import io.quarkus.websockets.next.test.utils.WSClient; +import io.smallrye.mutiny.Uni; +import io.vertx.core.Context; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; + +public class RuntimeErrorTest { + + @RegisterExtension + public static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot(root -> { + root.addClasses(Echo.class, RequestBean.class, WSClient.class); + }); + + @Inject + Vertx vertx; + + @TestHTTPResource("echo") + URI testUri; + + @Test + void testError() throws InterruptedException { + WSClient client = WSClient.create(vertx).connect(testUri); + client.send(Buffer.buffer("1")); + client.waitForMessages(1); + assertEquals("Something went wrong", client.getLastMessage().toString()); + assertTrue(RequestBean.DESTROYED_LATCH.await(5, TimeUnit.SECONDS)); + } + + @WebSocket(path = "/echo") + public static class Echo { + + @Inject + WebSocketConnection connection; + + @Inject + RequestBean requestBean; + + @OnBinaryMessage + void process(WebSocketConnection connection, Buffer message) { + requestBean.setState("ok"); + throw new IllegalStateException("Something went wrong"); + } + + @OnError + String encodingError(BinaryEncodeException e) { + return "Problem encoding: " + e.getEncodedObject().toString(); + } + + @OnError + String decodingError(BinaryDecodeException e) { + return "Problem decoding: " + e.getBytes().toString(); + } + + @OnError + Uni runtimeProblem(RuntimeException e, WebSocketConnection connection) { + assertTrue(Context.isOnEventLoopThread()); + assertEquals(connection.id(), this.connection.id()); + // The request context from @OnBinaryMessage is reused + assertEquals("ok", requestBean.getState()); + return connection.sendText(e.getMessage()); + } + + @OnError + String catchAll(Throwable e) { + return "Ooops!"; + } + + } + + @RequestScoped + public static class RequestBean { + + static final CountDownLatch DESTROYED_LATCH = new CountDownLatch(1); + + private volatile String state = "nok"; + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + @PreDestroy + void destroy() { + DESTROYED_LATCH.countDown(); + } + + } + +} diff --git a/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/RuntimeGlobalErrorTest.java b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/RuntimeGlobalErrorTest.java new file mode 100644 index 00000000000000..d492806d32c27a --- /dev/null +++ b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/RuntimeGlobalErrorTest.java @@ -0,0 +1,89 @@ +package io.quarkus.websockets.next.test.errors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.URI; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.quarkus.websockets.next.BinaryDecodeException; +import io.quarkus.websockets.next.BinaryEncodeException; +import io.quarkus.websockets.next.OnBinaryMessage; +import io.quarkus.websockets.next.OnError; +import io.quarkus.websockets.next.WebSocket; +import io.quarkus.websockets.next.WebSocketConnection; +import io.quarkus.websockets.next.test.utils.WSClient; +import io.smallrye.mutiny.Uni; +import io.vertx.core.Context; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; + +public class RuntimeGlobalErrorTest { + + @RegisterExtension + public static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot(root -> { + root.addClasses(Echo.class, WSClient.class); + }); + + @Inject + Vertx vertx; + + @TestHTTPResource("echo") + URI testUri; + + @Test + void testError() { + WSClient client = WSClient.create(vertx).connect(testUri); + client.send(Buffer.buffer("1")); + client.waitForMessages(1); + assertEquals("Global: Something went wrong", client.getLastMessage().toString()); + } + + @WebSocket(path = "/echo") + public static class Echo { + + @OnBinaryMessage + void process(WebSocketConnection connection, Buffer message) { + throw new IllegalStateException("Something went wrong"); + } + + @OnError + String encodingError(BinaryEncodeException e) { + return "Problem encoding: " + e.getEncodedObject().toString(); + } + + @OnError + String decodingError(BinaryDecodeException e) { + return "Problem decoding: " + e.getBytes().toString(); + } + + @OnError + Uni runtimeProblem(RuntimeException e, WebSocketConnection connection) { + return connection.sendText(e.getMessage()); + } + + @OnError + String catchAll(Throwable e) { + return "Ooops!"; + } + + } + + public static class GlobalErrorHandlers { + + @OnError + Uni runtimeProblem(IllegalStateException e, WebSocketConnection connection) { + assertTrue(Context.isOnEventLoopThread()); + return connection.sendText("Global: " + e.getMessage()); + } + + } + +} diff --git a/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/TextDecodeErrorTest.java b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/TextDecodeErrorTest.java new file mode 100644 index 00000000000000..95b27842e6b8fb --- /dev/null +++ b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/TextDecodeErrorTest.java @@ -0,0 +1,74 @@ +package io.quarkus.websockets.next.test.errors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.URI; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.quarkus.websockets.next.OnError; +import io.quarkus.websockets.next.OnTextMessage; +import io.quarkus.websockets.next.TextDecodeException; +import io.quarkus.websockets.next.WebSocket; +import io.quarkus.websockets.next.test.utils.WSClient; +import io.vertx.core.Vertx; +import io.vertx.mutiny.core.Context; + +public class TextDecodeErrorTest { + + @RegisterExtension + public static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot(root -> { + root.addClasses(Echo.class, WSClient.class); + }); + + @Inject + Vertx vertx; + + @TestHTTPResource("echo") + URI testUri; + + @Test + void testError() { + WSClient client = WSClient.create(vertx).connect(testUri); + client.send("not a json"); + client.waitForMessages(1); + assertEquals("Problem decoding: not a json", client.getLastMessage().toString()); + } + + @WebSocket(path = "/echo") + public static class Echo { + + @OnTextMessage + void process(Pojo pojo) { + } + + @OnError + String decodingError(TextDecodeException e) { + assertTrue(Context.isOnWorkerThread()); + return "Problem decoding: " + e.getText(); + } + + } + + public static class Pojo { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + } + +} diff --git a/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/TextEncodeErrorTest.java b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/TextEncodeErrorTest.java new file mode 100644 index 00000000000000..153219e9aea815 --- /dev/null +++ b/extensions/websockets-next/server/deployment/src/test/java/io/quarkus/websockets/next/test/errors/TextEncodeErrorTest.java @@ -0,0 +1,106 @@ +package io.quarkus.websockets.next.test.errors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.reflect.Type; +import java.net.URI; + +import jakarta.annotation.Priority; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.quarkus.websockets.next.OnError; +import io.quarkus.websockets.next.OnTextMessage; +import io.quarkus.websockets.next.TextEncodeException; +import io.quarkus.websockets.next.TextMessageCodec; +import io.quarkus.websockets.next.WebSocket; +import io.quarkus.websockets.next.test.utils.WSClient; +import io.vertx.core.Vertx; +import io.vertx.core.json.JsonObject; +import io.vertx.mutiny.core.Context; + +public class TextEncodeErrorTest { + + @RegisterExtension + public static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot(root -> { + root.addClasses(Echo.class, WSClient.class); + }); + + @Inject + Vertx vertx; + + @TestHTTPResource("echo") + URI testUri; + + @Test + void testError() { + WSClient client = WSClient.create(vertx).connect(testUri); + client.send(new JsonObject().put("name", "Fixa").encode()); + client.waitForMessages(1); + assertEquals("java.lang.IllegalArgumentException:Fixa", client.getLastMessage().toString()); + } + + @WebSocket(path = "/echo") + public static class Echo { + + @OnTextMessage(outputCodec = BadCodec.class) + Pojo process(Pojo pojo) { + return pojo; + } + + @OnError + String encodingError(TextEncodeException e) { + assertTrue(Context.isOnWorkerThread()); + return e.getCause().toString() + ":" + e.getEncodedObject().toString(); + } + + } + + @Priority(-1) // Let the JsonTextMessageCodec decode the pojo + @Singleton + public static class BadCodec implements TextMessageCodec { + + @Override + public boolean supports(Type type) { + return type.equals(Pojo.class); + } + + @Override + public String encode(Pojo value) { + throw new IllegalArgumentException(); + } + + @Override + public Pojo decode(Type type, String value) { + throw new UnsupportedOperationException(); + } + + } + + public static class Pojo { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + + } + +}