diff --git a/pom.xml b/pom.xml index 213a4f4a..e202f8fa 100644 --- a/pom.xml +++ b/pom.xml @@ -38,5 +38,6 @@ sample-binary samples-soap samples-db + samples-websocket diff --git a/samples-websocket/pom.xml b/samples-websocket/pom.xml new file mode 100644 index 00000000..a5d4532f --- /dev/null +++ b/samples-websocket/pom.xml @@ -0,0 +1,34 @@ + + + + + 4.0.0 + + org.citrusframework.samples + citrus-samples-websocket + 4.0.0 + Citrus Samples:: Websocket Samples + pom + + + sample-websocket-client + sample-websocket-server + + diff --git a/samples-websocket/sample-websocket-client/README.md b/samples-websocket/sample-websocket-client/README.md new file mode 100644 index 00000000..9847217c --- /dev/null +++ b/samples-websocket/sample-websocket-client/README.md @@ -0,0 +1,120 @@ +Websockets sample ![Logo][1] +============== + +This sample shows how to use the Citrus Websocket client to connect to a socket on the server and send/receive data. +Citrus Websocket features are also described in detail in [reference guide][4] + +Objectives +--------- + +The sample uses a small Quarkus application that provides a server side websocket for clients to connect to. +All messages sent to the socket get pushed to the connected clients. +Citrus is able to connect to the socket as a client in order to send/receive all messages via this socket broadcast. + +In the test Citrus will connect to the socket and send some data to it. +The same message is received in a next step to verify the message broadcast. + +We need a Websocket client component in the configuration: + +```java +@BindToRegistry +public WebSocketClient chatClient() { + return new WebSocketClientBuilder() + .requestUrl("ws://localhost:8081/chat/citrus") + .build(); +} +``` + +In the test cases we can reference this client component in order to send REST calls to the server. + +```java +t.when(send("http://localhost:8081/chat/citrus-user") + .message() + .fork(true) + .body("Hello from Citrus!")); +``` + +**NOTE:** The send action uses `fork=true` option. +This is because the send operation should not block the test to proceed and verify the server side socket communication. + +The Quarkus server socket should accept the connection and process the message sent by the Citrus client. +As a result of this we are able to verify the same message on the client because of the server socket broadcast. +This time the message has been adjusted by the Quarkus server with `>> {username}:` prefix. + +```java +t.then(receive() + .endpoint(chatClient) + .message() + .body(">> citrus: Hello Quarkus chat!")); +``` + +Run +--------- + +The sample application uses QuarkusTest as a framework to run the tests with JUnit Jupiter. +So you can compile, package and test the sample with Maven to run the test. + +```shell +mvn clean verify +``` + +This executes the complete Maven build lifecycle. +The Citrus test cases are part of the unit testing lifecycle and get executed automatically. +The Quarkus application is also started automatically as part of the test. + +During the build you will see Citrus performing some integration tests. + +System under test +--------- + +The sample uses a small Quarkus application that provides the Websocket implementation. +You can read more about Quarkus websocket support in [https://quarkus.io/guides/websockets](https://quarkus.io/guides/websockets). + +Up to now we have started the Quarkus sample application as part of the unit test during the Maven build lifecycle. +This approach is fantastic when running automated tests in a continuous build. + +There may be times we want to test against a standalone application. + +You can start the sample Quarkus application in DevServices mode with this command. + +```shell +mvn quarkus:dev +``` + +Now we are ready to execute some Citrus tests in a separate JVM. + +Citrus test +--------- + +Once the sample application is deployed and running you can execute the Citrus test cases. +Open a separate command line terminal and navigate to the sample folder. + +Execute all Citrus tests by calling + +```shell +mvn verify +``` + +You can also pick a single test by calling + +```shell +mvn clean verify -Dtest= +``` + +You should see Citrus performing several tests with lots of debugging output in both terminals (sample application +and Citrus test client). +And of course green tests at the very end of the build. + +Of course, you can also start the Citrus tests from your favorite IDE. +Just start the Citrus test using the JUnit Jupiter IDE integration in IntelliJ, Eclipse or Netbeans. + +Further information +--------- + +For more information on Citrus see [www.citrusframework.org][2], including +a complete [reference manual][3]. + + [1]: https://citrusframework.org/img/brand-logo.png "Citrus" + [2]: https://citrusframework.org + [3]: https://citrusframework.org/reference/html/ + [4]: https://citrusframework.org/reference/html#websocket diff --git a/samples-websocket/sample-websocket-client/pom.xml b/samples-websocket/sample-websocket-client/pom.xml new file mode 100644 index 00000000..ba0203f6 --- /dev/null +++ b/samples-websocket/sample-websocket-client/pom.xml @@ -0,0 +1,160 @@ + + + + 4.0.0 + + org.citrusframework.samples + citrus-sample-websocket-client + Citrus Samples:: Websocket Client + 4.0.0 + + + UTF-8 + UTF-8 + + 3.11.0 + 3.1.2 + + 3.6.6 + 4.0.2 + + + + + + io.quarkus.platform + quarkus-bom + ${quarkus.platform.version} + pom + import + + + org.citrusframework + citrus-bom + ${citrus.version} + pom + import + + + + + + + + io.quarkus + quarkus-websockets + + + + + io.quarkus + quarkus-junit5 + test + + + + + org.citrusframework + citrus-quarkus + test + + + org.citrusframework + citrus-websocket + test + + + org.citrusframework + citrus-validation-text + test + + + + + + + io.quarkus.platform + quarkus-maven-plugin + ${quarkus.platform.version} + + + 9092 + + + true + + + + build + generate-code + generate-code-tests + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${compiler-plugin.version} + + ${project.build.sourceEncoding} + + -parameters + + 17 + 17 + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + diff --git a/samples-websocket/sample-websocket-client/src/main/java/org/citrusframework/samples/websocket/ChatSocket.java b/samples-websocket/sample-websocket-client/src/main/java/org/citrusframework/samples/websocket/ChatSocket.java new file mode 100644 index 00000000..7dd9f283 --- /dev/null +++ b/samples-websocket/sample-websocket-client/src/main/java/org/citrusframework/samples/websocket/ChatSocket.java @@ -0,0 +1,81 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.citrusframework.samples.websocket; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.websocket.OnClose; +import jakarta.websocket.OnError; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.Session; +import jakarta.websocket.server.PathParam; +import jakarta.websocket.server.ServerEndpoint; + +import org.jboss.logging.Logger; + +@ServerEndpoint("/chat/{username}") +@ApplicationScoped +public class ChatSocket { + + private static final Logger LOG = Logger.getLogger(ChatSocket.class); + + Map sessions = new ConcurrentHashMap<>(); + + @OnOpen + public void onOpen(Session session, @PathParam("username") String username) { + sessions.put(username, session); + } + + @OnClose + public void onClose(Session session, @PathParam("username") String username) { + sessions.remove(username); + broadcast("User " + username + " left"); + } + + @OnError + public void onError(Session session, @PathParam("username") String username, Throwable throwable) { + sessions.remove(username); + LOG.error("onError", throwable); + broadcast("User " + username + " left on error: " + throwable); + } + + @OnMessage + public void onMessage(String message, @PathParam("username") String username) { + if (message.equalsIgnoreCase("_ready_")) { + broadcast("User " + username + " joined"); + } else { + broadcast(">> " + username + ": " + message); + } + } + + private void broadcast(String message) { + sessions.values().forEach(s -> { + s.getAsyncRemote().sendObject(message, result -> { + if (result.getException() != null) { + System.out.println("Unable to send message: " + result.getException()); + } + }); + }); + } + +} diff --git a/samples-websocket/sample-websocket-client/src/main/resources/application.properties b/samples-websocket/sample-websocket-client/src/main/resources/application.properties new file mode 100644 index 00000000..c0824d2d --- /dev/null +++ b/samples-websocket/sample-websocket-client/src/main/resources/application.properties @@ -0,0 +1,21 @@ +# +# Copyright 2024 the original author or authors. +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +quarkus.log.level=INFO +quarkus.arc.ignored-split-packages=org.citrusframework.* diff --git a/samples-websocket/sample-websocket-client/src/test/java/org/citrusframework/samples/websocket/ChatSocketTest.java b/samples-websocket/sample-websocket-client/src/test/java/org/citrusframework/samples/websocket/ChatSocketTest.java new file mode 100644 index 00000000..d0d4f495 --- /dev/null +++ b/samples-websocket/sample-websocket-client/src/test/java/org/citrusframework/samples/websocket/ChatSocketTest.java @@ -0,0 +1,69 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.citrusframework.samples.websocket; + +import io.quarkus.test.junit.QuarkusTest; +import org.citrusframework.TestCaseRunner; +import org.citrusframework.annotations.CitrusConfiguration; +import org.citrusframework.annotations.CitrusEndpoint; +import org.citrusframework.annotations.CitrusResource; +import org.citrusframework.quarkus.CitrusSupport; +import org.citrusframework.spi.BindToRegistry; +import org.citrusframework.websocket.client.WebSocketClient; +import org.citrusframework.websocket.client.WebSocketClientBuilder; +import org.junit.jupiter.api.Test; + +import static org.citrusframework.actions.ReceiveMessageAction.Builder.receive; +import static org.citrusframework.actions.SendMessageAction.Builder.send; + +@QuarkusTest +@CitrusSupport +@CitrusConfiguration( classes = { ChatSocketTest.EndpointConfig.class } ) +class ChatSocketTest { + + @CitrusResource + TestCaseRunner t; + + @CitrusEndpoint + WebSocketClient chatClient; + + @Test + void shouldConnectAndSendMessage() { + t.given(send() + .endpoint(chatClient) + .message() + .body("Hello Quarkus chat!")); + + t.then(receive() + .endpoint(chatClient) + .message() + .body(">> citrus: Hello Quarkus chat!")); + } + + public static class EndpointConfig { + + @BindToRegistry + public WebSocketClient chatClient() { + return new WebSocketClientBuilder() + .requestUrl("ws://localhost:8081/chat/citrus") + .build(); + } + } +} diff --git a/samples-websocket/sample-websocket-server/README.md b/samples-websocket/sample-websocket-server/README.md new file mode 100644 index 00000000..8212f717 --- /dev/null +++ b/samples-websocket/sample-websocket-server/README.md @@ -0,0 +1,170 @@ +Websockets sample ![Logo][1] +============== + +This sample shows how to use the Citrus Websocket server to provide a socket on the server so that clients can connect to and send/receive data. +Citrus Websocket features are also described in detail in [reference guide][4] + +Objectives +--------- + +The sample uses a small Quarkus application that provides a websocket client to connect to the Citrus Websocket server. +All messages sent to the socket get pushed to the connected clients. +Citrus is able to start the socket as a server in order to accept client sessions and broadcast messages to all connected clients. + +In the test Citrus will verify client connections and broadcast some data to the clients. + +We need a Websocket server component in the configuration: + +```java +public static class EndpointConfig { + + private WebSocketEndpoint chatEndpoint; + + @BindToRegistry + public WebSocketEndpoint chatEndpoint() { + if (chatEndpoint == null) { + WebSocketServerEndpointConfiguration chatEndpointConfig = new WebSocketServerEndpointConfiguration(); + chatEndpointConfig.setEndpointUri("/chat"); + chatEndpoint = new WebSocketEndpoint(chatEndpointConfig); + } + + return chatEndpoint; + } + + @BindToRegistry + public WebSocketServer chatServer() { + return new WebSocketServerBuilder() + .webSockets(Collections.singletonList(chatEndpoint())) + .port(8088) + .autoStart(true) + .build(); + } +} +``` + +The server listens on port `8088` and provides a websocket endpoint on `/chat`. +So clients may connect to the socket opening sessions on `http://localhost:8088/chat`. + +In the test cases we can receive client sessions with a normal receive action that references the websocket endpoint. + +```java +t.then(receive() + .endpoint(chatEndpoint) + .message() + .body("Quarkus wants to join ...")); +``` + +**NOTE:** The message `Quarkus wants to join ...` is automatically sent by the sample Quarkus application when the session is opened. +We can respond with a server side message that is sent to all connected clients. + +```java +t.then(send() + .endpoint(chatEndpoint) + .message() + .body("Welcome Quarkus!")); +``` + +You will see this response printed to the log output of the Quarkus sample application. + +The test is able to trigger some client messages on the Quarkus application by sending a Http POST request to the REST API of the Quarkus application. + +```java +t.when(http().client("http://localhost:8081") + .send() + .post("chat/citrus-user") + .fork(true) + .message() + .body("Hello from Citrus!")); +``` + +**NOTE:** The test uses a dynamic Http endpoint URL to send the POST request. +The username is given as a path parameter and the message body represents the message that is sent to the websocket. + +Now the test is able to verify the websocket message on the server. +This time the message has been adjusted by the Quarkus client with `>> {username}:` prefix. + +```java +t.then(receive() + .endpoint(chatEndpoint) + .message() + .body(">> citrus-user: Hello from Citrus!")); +``` + +At the very end of the test we can verify the response of the Http POST request. + +```java +t.then(http().client("http://localhost:8081") + .receive() + .response(HttpStatus.CREATED)); +``` + +Run +--------- + +The sample application uses QuarkusTest as a framework to run the tests with JUnit Jupiter. +So you can compile, package and test the sample with Maven to run the test. + +```shell +mvn clean verify +``` + +This executes the complete Maven build lifecycle. +The Citrus test cases are part of the unit testing lifecycle and get executed automatically. +The Quarkus application is also started automatically as part of the test. + +During the build you will see Citrus performing some integration tests. + +System under test +--------- + +The sample uses a small Quarkus application that provides the Websocket implementation. +You can read more about Quarkus websocket support in [https://quarkus.io/guides/websockets](https://quarkus.io/guides/websockets). + +Up to now we have started the Quarkus sample application as part of the unit test during the Maven build lifecycle. +This approach is fantastic when running automated tests in a continuous build. + +There may be times we want to test against a standalone application. + +You can start the sample Quarkus application in DevServices mode with this command. + +```shell +mvn quarkus:dev +``` + +Now we are ready to execute some Citrus tests in a separate JVM. + +Citrus test +--------- + +Once the sample application is deployed and running you can execute the Citrus test cases. +Open a separate command line terminal and navigate to the sample folder. + +Execute all Citrus tests by calling + +```shell +mvn verify +``` + +You can also pick a single test by calling + +```shell +mvn clean verify -Dtest= +``` + +You should see Citrus performing several tests with lots of debugging output in both terminals (sample application +and Citrus test client). +And of course green tests at the very end of the build. + +Of course, you can also start the Citrus tests from your favorite IDE. +Just start the Citrus test using the JUnit Jupiter IDE integration in IntelliJ, Eclipse or Netbeans. + +Further information +--------- + +For more information on Citrus see [www.citrusframework.org][2], including +a complete [reference manual][3]. + + [1]: https://citrusframework.org/img/brand-logo.png "Citrus" + [2]: https://citrusframework.org + [3]: https://citrusframework.org/reference/html/ + [4]: https://citrusframework.org/reference/html#websocket diff --git a/samples-websocket/sample-websocket-server/pom.xml b/samples-websocket/sample-websocket-server/pom.xml new file mode 100644 index 00000000..2bdbf9fd --- /dev/null +++ b/samples-websocket/sample-websocket-server/pom.xml @@ -0,0 +1,168 @@ + + + + 4.0.0 + + org.citrusframework.samples + citrus-sample-websocket-server + Citrus Samples:: Websocket Server + 4.0.0 + + + UTF-8 + UTF-8 + + 3.11.0 + 3.1.2 + + 3.6.6 + 4.0.2 + + + + + + io.quarkus.platform + quarkus-bom + ${quarkus.platform.version} + pom + import + + + org.citrusframework + citrus-bom + ${citrus.version} + pom + import + + + + + + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-websockets + + + + + io.quarkus + quarkus-junit5 + test + + + + + org.citrusframework + citrus-quarkus + test + + + org.citrusframework + citrus-websocket + test + + + org.citrusframework + citrus-validation-text + test + + + + + + + io.quarkus.platform + quarkus-maven-plugin + ${quarkus.platform.version} + + + 9092 + + + true + + + + build + generate-code + generate-code-tests + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${compiler-plugin.version} + + ${project.build.sourceEncoding} + + -parameters + + 17 + 17 + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + diff --git a/samples-websocket/sample-websocket-server/src/main/java/org/citrusframework/samples/websocket/ChatResource.java b/samples-websocket/sample-websocket-server/src/main/java/org/citrusframework/samples/websocket/ChatResource.java new file mode 100644 index 00000000..8bbd1c70 --- /dev/null +++ b/samples-websocket/sample-websocket-server/src/main/java/org/citrusframework/samples/websocket/ChatResource.java @@ -0,0 +1,44 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.citrusframework.samples.websocket; + +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +@Path("chat/{username}") +public class ChatResource { + + @Inject + ChatService chatService; + + @POST + @Produces("text/plain") + @Consumes(MediaType.TEXT_PLAIN) + public Response add(@PathParam("username") String user, String message) { + chatService.send(user, message); + return Response.status(Response.Status.CREATED).build(); + } +} diff --git a/samples-websocket/sample-websocket-server/src/main/java/org/citrusframework/samples/websocket/ChatService.java b/samples-websocket/sample-websocket-server/src/main/java/org/citrusframework/samples/websocket/ChatService.java new file mode 100644 index 00000000..f4cbfc66 --- /dev/null +++ b/samples-websocket/sample-websocket-server/src/main/java/org/citrusframework/samples/websocket/ChatService.java @@ -0,0 +1,76 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.citrusframework.samples.websocket; + +import java.io.IOException; +import java.net.URI; + +import jakarta.inject.Singleton; +import jakarta.websocket.ClientEndpoint; +import jakarta.websocket.ContainerProvider; +import jakarta.websocket.DeploymentException; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.Session; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.jboss.logging.Logger; + +@Singleton +public class ChatService { + + private static final Logger LOG = Logger.getLogger(ChatService.class); + + @ConfigProperty(name = "chat.websocket.uri", defaultValue = "http://localhost:8088/chat") + URI uri; + + private Session session; + + public void send(String user, String message) { + openSession().getAsyncRemote().sendText(">> %s: %s".formatted(user, message)); + } + + private Session openSession() { + if (session == null) { + try { + session = ContainerProvider.getWebSocketContainer().connectToServer(ChatClient.class, uri); + } catch (DeploymentException | IOException e) { + throw new RuntimeException(e); + } + } + + return session; + } + + @ClientEndpoint + public static class ChatClient { + + @OnOpen + public void open(Session session) { + LOG.info("CONNECTED!"); + session.getAsyncRemote().sendText("Quarkus wants to join ..."); + } + + @OnMessage + void message(String msg) { + LOG.info(msg); + } + + } +} diff --git a/samples-websocket/sample-websocket-server/src/main/resources/application.properties b/samples-websocket/sample-websocket-server/src/main/resources/application.properties new file mode 100644 index 00000000..c0824d2d --- /dev/null +++ b/samples-websocket/sample-websocket-server/src/main/resources/application.properties @@ -0,0 +1,21 @@ +# +# Copyright 2024 the original author or authors. +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +quarkus.log.level=INFO +quarkus.arc.ignored-split-packages=org.citrusframework.* diff --git a/samples-websocket/sample-websocket-server/src/test/java/org/citrusframework/samples/websocket/ChatSocketTest.java b/samples-websocket/sample-websocket-server/src/test/java/org/citrusframework/samples/websocket/ChatSocketTest.java new file mode 100644 index 00000000..0f6e397f --- /dev/null +++ b/samples-websocket/sample-websocket-server/src/test/java/org/citrusframework/samples/websocket/ChatSocketTest.java @@ -0,0 +1,107 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.citrusframework.samples.websocket; + +import java.util.Collections; + +import io.quarkus.test.junit.QuarkusTest; +import org.citrusframework.TestCaseRunner; +import org.citrusframework.annotations.CitrusConfiguration; +import org.citrusframework.annotations.CitrusEndpoint; +import org.citrusframework.annotations.CitrusResource; +import org.citrusframework.quarkus.CitrusSupport; +import org.citrusframework.spi.BindToRegistry; +import org.citrusframework.websocket.endpoint.WebSocketEndpoint; +import org.citrusframework.websocket.server.WebSocketServer; +import org.citrusframework.websocket.server.WebSocketServerBuilder; +import org.citrusframework.websocket.server.WebSocketServerEndpointConfiguration; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; + +import static org.citrusframework.actions.ReceiveMessageAction.Builder.receive; +import static org.citrusframework.actions.SendMessageAction.Builder.send; +import static org.citrusframework.http.actions.HttpActionBuilder.http; + +@QuarkusTest +@CitrusSupport +@CitrusConfiguration(classes = { ChatSocketTest.EndpointConfig.class }) +class ChatSocketTest { + + @CitrusResource + TestCaseRunner t; + + @CitrusEndpoint + WebSocketEndpoint chatEndpoint; + + @Test + void shouldBroadcastMessages() { + t.when(http() + .client("http://localhost:8081") + .send() + .post("chat/citrus-user") + .fork(true) + .message() + .body("Hello from Citrus!")); + + t.then(receive() + .endpoint(chatEndpoint) + .message() + .body("Quarkus wants to join ...")); + + t.then(send() + .endpoint(chatEndpoint) + .message() + .body("Welcome Quarkus!")); + + t.then(receive() + .endpoint(chatEndpoint) + .message() + .body(">> citrus-user: Hello from Citrus!")); + + t.then(http().client("http://localhost:8081") + .receive() + .response(HttpStatus.CREATED)); + } + + public static class EndpointConfig { + + private WebSocketEndpoint chatEndpoint; + + @BindToRegistry + public WebSocketEndpoint chatEndpoint() { + if (chatEndpoint == null) { + WebSocketServerEndpointConfiguration chatEndpointConfig = new WebSocketServerEndpointConfiguration(); + chatEndpointConfig.setEndpointUri("/chat"); + chatEndpoint = new WebSocketEndpoint(chatEndpointConfig); + } + + return chatEndpoint; + } + + @BindToRegistry + public WebSocketServer chatServer() { + return new WebSocketServerBuilder() + .webSockets(Collections.singletonList(chatEndpoint())) + .port(8088) + .autoStart(true) + .build(); + } + } +}