From 97c8f115a04c98d2f39c755ac94d1749c588bf4c Mon Sep 17 00:00:00 2001 From: fdelpoggio Date: Thu, 17 Jul 2025 11:30:45 -0300 Subject: [PATCH 01/11] Upgrade JMeter to 5.6.3 --- jmeter-java-dsl/pom.xml | 6 +++--- .../javadsl/core/controllers/FragmentMethodCall.java | 4 ++-- .../jmeter/javadsl/http/DslBaseHttpSampler.java | 5 +---- .../javadsl/codegeneration/DslCodeGeneratorTest.java | 6 +++--- .../jmeter/javadsl/core/StringTemplateAssert.java | 2 +- .../core/controllers/DslIncludeControllerTest.java | 4 ++-- .../core/controllers/DslModuleControllerTest.java | 10 +++++----- .../controllers/DslTestFragmentControllerTest.java | 12 ++++++------ .../jmeter/javadsl/http/DslHttpSamplerTest.java | 2 +- .../src/test/resources/test-plan.template.jmx | 2 +- pom.xml | 2 +- 11 files changed, 26 insertions(+), 29 deletions(-) diff --git a/jmeter-java-dsl/pom.xml b/jmeter-java-dsl/pom.xml index f4ce75b9..000b5c4a 100644 --- a/jmeter-java-dsl/pom.xml +++ b/jmeter-java-dsl/pom.xml @@ -89,7 +89,7 @@ commons-io commons-io - 2.11.0 + 2.15.1 org.apache.commons @@ -114,7 +114,7 @@ org.apache.commons commons-pool2 - 2.11.1 + 2.12.0 @@ -182,4 +182,4 @@ - + \ No newline at end of file diff --git a/jmeter-java-dsl/src/main/java/us/abstracta/jmeter/javadsl/core/controllers/FragmentMethodCall.java b/jmeter-java-dsl/src/main/java/us/abstracta/jmeter/javadsl/core/controllers/FragmentMethodCall.java index 4f419dee..c675fd37 100644 --- a/jmeter-java-dsl/src/main/java/us/abstracta/jmeter/javadsl/core/controllers/FragmentMethodCall.java +++ b/jmeter-java-dsl/src/main/java/us/abstracta/jmeter/javadsl/core/controllers/FragmentMethodCall.java @@ -1,8 +1,8 @@ package us.abstracta.jmeter.javadsl.core.controllers; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; import org.apache.jmeter.testelement.TestElement; @@ -51,7 +51,7 @@ private static String solveMethodName(TestElement element, MethodCallContext con } private static Map getDefinedMethods(MethodCallContext context) { - return context.getRoot().computeEntryIfAbsent(FragmentMethodCall.class, HashMap::new); + return context.getRoot().computeEntryIfAbsent(FragmentMethodCall.class, IdentityHashMap::new); } private static String buildUniqueName(String elementName, Set existingNames) { diff --git a/jmeter-java-dsl/src/main/java/us/abstracta/jmeter/javadsl/http/DslBaseHttpSampler.java b/jmeter-java-dsl/src/main/java/us/abstracta/jmeter/javadsl/http/DslBaseHttpSampler.java index ea1d4e90..73937b82 100644 --- a/jmeter-java-dsl/src/main/java/us/abstracta/jmeter/javadsl/http/DslBaseHttpSampler.java +++ b/jmeter-java-dsl/src/main/java/us/abstracta/jmeter/javadsl/http/DslBaseHttpSampler.java @@ -253,10 +253,7 @@ protected TestElement buildTestElement() { } HTTPSamplerProxy ret = new HTTPSamplerProxy(); HttpElementHelper.modifyTestElementUrl(ret, protocol, host, port, path); - // We need to use this logic since setPath method triggers additional logic - if (path != null) { - ret.setPath(path); - } + HttpElementHelper.modifyTestElementTimeouts(ret, connectionTimeout, responseTimeout); HttpElementHelper.modifyTestElementProxy(ret, proxyUrl, proxyUser, proxyPassword); return configureHttpTestElement(ret); diff --git a/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/codegeneration/DslCodeGeneratorTest.java b/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/codegeneration/DslCodeGeneratorTest.java index 64bb6b41..f9dae8da 100644 --- a/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/codegeneration/DslCodeGeneratorTest.java +++ b/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/codegeneration/DslCodeGeneratorTest.java @@ -24,7 +24,7 @@ public void shouldGenerateExpectedCodeWhenSimpleJmxIsProvided(@TempDir Path temp throws Exception { File solvedTemplate = solveTemplateResource("test-plan.template.jmx", tempDir); assertThat(new DslCodeGenerator().generateCodeFromJmx(solvedTemplate)) - .isEqualTo( + .isEqualToNormalizingNewlines( solveTestClassTemplate(Collections.singleton(ContentType.class.getName()), "SimpleTest.java")); } @@ -50,7 +50,7 @@ private String solveTestClassTemplate(Set imports, String testPlanCodeRe public void shouldGenerateExpectedCodeWhenRecordedJmxIsProvided() throws Exception { assertThat(new DslCodeGenerator() .generateCodeFromJmx(new TestResource(RESOURCES_FOLDER + "/recorded.jmx").file())) - .isEqualTo(solveTestClassTemplate(Collections.singleton(StandardCharsets.class.getName()), + .isEqualToNormalizingNewlines(solveTestClassTemplate(Collections.singleton(StandardCharsets.class.getName()), "RecordedTest.java")); } @@ -58,7 +58,7 @@ public void shouldGenerateExpectedCodeWhenRecordedJmxIsProvided() throws Excepti public void shouldGenerateCommentedElementsCodeWhenDisabledElementsInJmx() throws Exception { assertThat(new DslCodeGenerator() .generateCodeFromJmx(new TestResource(RESOURCES_FOLDER + "/disabled-elements.jmx").file())) - .isEqualTo(solveTestClassTemplate(Collections.emptySet(), "DisabledElements.java")); + .isEqualToNormalizingNewlines(solveTestClassTemplate(Collections.emptySet(), "DisabledElements.java")); } } diff --git a/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/StringTemplateAssert.java b/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/StringTemplateAssert.java index 3ed8e133..649be002 100644 --- a/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/StringTemplateAssert.java +++ b/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/StringTemplateAssert.java @@ -43,7 +43,7 @@ public static StringTemplateAssertString assertThat(String actual) { protected abstract ErrorMessageFactory getErrorMessageFactory(List> diffs); public SELF matches(TestResource template) throws IOException { - return matches(template.rawContents()); + return matches(template.rawContents().replace("\r\n", "\n")); } public SELF matches(String templateContents) throws IOException { diff --git a/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/controllers/DslIncludeControllerTest.java b/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/controllers/DslIncludeControllerTest.java index 0e93c427..270cae75 100644 --- a/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/controllers/DslIncludeControllerTest.java +++ b/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/controllers/DslIncludeControllerTest.java @@ -33,7 +33,7 @@ public void shouldGenerateDslWithFragmentWhenConvertingTestPlanWithIncludeContro buildIncludeControllerJmx(includedJmx) )); assertThat(jmx2dsl(jmx, tmp)) - .isEqualTo(buildTestPlanDsl(FRAGMENT_METHOD_CALL)); + .isEqualToNormalizingNewlines(buildTestPlanDsl(FRAGMENT_METHOD_CALL)); } private File buildIncludedTestPlanJmx(Path tmp) throws IOException { @@ -69,7 +69,7 @@ public void shouldReuseFragmentWhenTestPlanWithTwoIncludesWithSameFile(@TempDir buildIncludeControllerJmx(includedJmx) )); assertThat(jmx2dsl(jmx, tmp)) - .isEqualTo(buildTestPlanDsl(FRAGMENT_METHOD_CALL, FRAGMENT_METHOD_CALL)); + .isEqualToNormalizingNewlines(buildTestPlanDsl(FRAGMENT_METHOD_CALL, FRAGMENT_METHOD_CALL)); } } diff --git a/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/controllers/DslModuleControllerTest.java b/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/controllers/DslModuleControllerTest.java index f8a38eb4..f70b9e22 100644 --- a/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/controllers/DslModuleControllerTest.java +++ b/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/controllers/DslModuleControllerTest.java @@ -31,7 +31,7 @@ public void shouldReuseFragmentMethodWhenModuleUsesPreviousEnabledFragment(@Temp buildFragmentJmx(), buildModuleJmx(DEFAULT_FRAGMENT_NAME)); assertThat(jmx2dsl(jmx, tmp)) - .isEqualTo(buildFragmentPlanDsl( + .isEqualToNormalizingNewlines(buildFragmentPlanDsl( buildThreadGroupDsl(FRAGMENT_METHOD_CALL, FRAGMENT_METHOD_CALL))); } @@ -60,7 +60,7 @@ public void shouldDefineAndUseFragmentMethodWhenModuleUsesLaterEnabledFragment( buildModuleJmx(DEFAULT_FRAGMENT_NAME), buildFragmentJmx()); assertThat(jmx2dsl(jmx, tmp)) - .isEqualTo(buildFragmentPlanDsl( + .isEqualToNormalizingNewlines(buildFragmentPlanDsl( buildThreadGroupDsl(FRAGMENT_METHOD_CALL, FRAGMENT_METHOD_CALL))); } @@ -72,7 +72,7 @@ public void shouldUseFragmentMethodWhenModuleUsesDisabledFragment(@TempDir Path buildModuleJmx(DEFAULT_FRAGMENT_NAME) ); assertThat(jmx2dsl(jmx, tmp)) - .isEqualTo(buildFragmentPlanDsl( + .isEqualToNormalizingNewlines(buildFragmentPlanDsl( "httpCookies()", "httpCache()", buildThreadGroupDsl("//" + FRAGMENT_METHOD_CALL, FRAGMENT_METHOD_CALL))); @@ -85,7 +85,7 @@ public void shouldDefineAndUseMethodWhenModuleUsesPreviouslyDefinedController(@T buildIfControllerJmx(), buildModuleJmx(IF_CONTROLLER_DEFAULT_NAME)); assertThat(jmx2dsl(jmx, tmp)) - .isEqualTo( + .isEqualToNormalizingNewlines( buildIfControllerPlanDsl(IF_CONTROLLER_METHOD_CALL, IF_CONTROLLER_METHOD_CALL)); } @@ -109,7 +109,7 @@ public void shouldDefineAndUseMethodWhenModuleUsesLaterDefinedController(@TempDi buildIfControllerJmx() ); assertThat(jmx2dsl(jmx, tmp)) - .isEqualTo( + .isEqualToNormalizingNewlines( buildIfControllerPlanDsl(IF_CONTROLLER_METHOD_CALL, IF_CONTROLLER_METHOD_CALL)); } diff --git a/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/controllers/DslTestFragmentControllerTest.java b/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/controllers/DslTestFragmentControllerTest.java index 7b497070..99721ca7 100644 --- a/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/controllers/DslTestFragmentControllerTest.java +++ b/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/controllers/DslTestFragmentControllerTest.java @@ -57,7 +57,7 @@ public void shouldGenerateDslWithFragmentMethodWhenConvertTestPlanWithFragment( @TempDir Path tmp) throws IOException { String testPlanJmx = buildTestPlanJmx(buildFragmentJmx()); assertThat(jmx2dsl(testPlanJmx, tmp)) - .isEqualTo(buildTestPlanDsl(buildFragmentMethod(), DEFAULT_FRAGMENT_METHOD_CALL)); + .isEqualToNormalizingNewlines(buildTestPlanDsl(buildFragmentMethod(), DEFAULT_FRAGMENT_METHOD_CALL)); } private String buildTestPlanDsl(String method, String child) { @@ -79,7 +79,7 @@ public void shouldGenerateDslWithFragmentNameWhenConvertFragmentNonDefaultName( String testPlanJmx = buildTestPlanJmx(buildFragmentJmx(fragmentName)); String methodName = "myFragment"; assertThat(jmx2dsl(testPlanJmx, tmp)) - .isEqualTo(buildTestPlanDsl( + .isEqualToNormalizingNewlines(buildTestPlanDsl( buildFragmentMethod(methodName, fragmentName), methodName + "()")); } @@ -91,7 +91,7 @@ public void shouldGenerateDslWithFragmentNameWhenConvertFragmentWithNameStarting String testPlanJmx = buildTestPlanJmx(buildFragmentJmx(fragmentName)); String methodName = "fragment" + fragmentName; assertThat(jmx2dsl(testPlanJmx, tmp)) - .isEqualTo(buildTestPlanDsl( + .isEqualToNormalizingNewlines(buildTestPlanDsl( buildFragmentMethod(methodName, fragmentName), methodName + "()")); } @@ -103,7 +103,7 @@ public void shouldGenerateDslWithFragmentNameWhenConvertFragmentWithNameWithSpec String testPlanJmx = buildTestPlanJmx(buildFragmentJmx(fragmentName)); String methodName = "myFragment"; assertThat(jmx2dsl(testPlanJmx, tmp)) - .isEqualTo(buildTestPlanDsl( + .isEqualToNormalizingNewlines(buildTestPlanDsl( buildFragmentMethod(methodName, fragmentName), methodName + "()")); } @@ -116,7 +116,7 @@ public void shouldGenerateDslWithFragmentsWhenConvertFragmentsWithCollidingNames buildFragmentJmx()); String methodName2 = "testFragment2"; assertThat(jmx2dsl(testPlanJmx, tmp)) - .isEqualTo(buildTestPlanDsl( + .isEqualToNormalizingNewlines(buildTestPlanDsl( Arrays.asList( buildFragmentMethod(), buildFragmentMethod(methodName2, DEFAULT_FRAGMENT_NAME)), @@ -130,7 +130,7 @@ public void shouldGenerateDslWithCommentedFragmentCallWhenConvertDisabledFragmen String testPlanJmx = buildTestPlanJmx(buildFragmentDisabledJmx()); String cacheMethodCall = "httpCache()"; assertThat(jmx2dsl(testPlanJmx, tmp)) - .isEqualTo(buildTestPlanDsl(Collections.singletonList(buildFragmentMethod()), + .isEqualToNormalizingNewlines(buildTestPlanDsl(Collections.singletonList(buildFragmentMethod()), Arrays.asList("httpCookies()", cacheMethodCall, "//" + DEFAULT_FRAGMENT_METHOD_CALL)) .replace(cacheMethodCall + ",", cacheMethodCall + "//,")); } diff --git a/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/http/DslHttpSamplerTest.java b/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/http/DslHttpSamplerTest.java index 9d8ad7a2..ebed77f7 100644 --- a/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/http/DslHttpSamplerTest.java +++ b/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/http/DslHttpSamplerTest.java @@ -450,7 +450,7 @@ public void shouldSendMultiPartFormWhenPostRequestWithBodyParts() throws Excepti verify(postRequestedFor(anyUrl()) .withHeader(HTTPConstants.HEADER_CONTENT_TYPE, matching(ContentType.MULTIPART_FORM_DATA.withCharset((String) null) + "; boundary=" - + MULTIPART_BOUNDARY_PATTERN)) + + MULTIPART_BOUNDARY_PATTERN + "; charset=UTF-8")) .withRequestBody(matching( buildMultiPartBodyPattern(part1Name, part1Value, part1Encoding, part2Name, part2File, part2Encoding)))); diff --git a/jmeter-java-dsl/src/test/resources/test-plan.template.jmx b/jmeter-java-dsl/src/test/resources/test-plan.template.jmx index 073d8edb..639e435b 100644 --- a/jmeter-java-dsl/src/test/resources/test-plan.template.jmx +++ b/jmeter-java-dsl/src/test/resources/test-plan.template.jmx @@ -1,5 +1,5 @@ - + diff --git a/pom.xml b/pom.xml index b33f39a4..52afdd02 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ 1729085386 1.8 1.8 - 5.5 + 5.6.3 1.17.6 2.35.0 5.9.1 From cb80fd80763560d31c76bfadd695ec5100b53ef0 Mon Sep 17 00:00:00 2001 From: fdelpoggio Date: Tue, 28 Oct 2025 10:52:13 -0300 Subject: [PATCH 02/11] Add WebSocket support to JMeterDSL --- docs/guide/protocols/websocket/index.md | 210 ++++++++ jmeter-java-dsl-cli/pom.xml | 1 - .../jmeter/javadsl/jmx2dsl/Jmx2DslIT.java | 4 +- jmeter-java-dsl-websocket/pom.xml | 39 ++ .../websocket/DslWebsocketSampler.java | 463 ++++++++++++++++++ .../java/DslWebsocketCodeGeneratorTest.java | 86 ++++ .../test/java/DslWebsocketSamplerTest.java | 217 ++++++++ .../ComplexWebSocketTest.java | 14 + .../DisabledWebSocketElements.java | 16 + .../DisabledWebSocketElementsTest.java | 28 ++ .../SimpleWebSocketTest.java | 14 + .../WebSocketWithAssertionsTest.java | 18 + .../WebSocketWithVariablesTest.java | 18 + .../complex-websocket.jmx | 68 +++ .../disabled-websocket-elements.jmx | 84 ++++ .../websocket-test-plan.template.jmx | 71 +++ .../websocket-with-assertions.jmx | 82 ++++ .../websocket-with-variables.jmx | 100 ++++ pom.xml | 1 + 19 files changed, 1531 insertions(+), 3 deletions(-) create mode 100644 docs/guide/protocols/websocket/index.md create mode 100644 jmeter-java-dsl-websocket/pom.xml create mode 100644 jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java create mode 100644 jmeter-java-dsl-websocket/src/test/java/DslWebsocketCodeGeneratorTest.java create mode 100644 jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java create mode 100644 jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/ComplexWebSocketTest.java create mode 100644 jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElements.java create mode 100644 jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElementsTest.java create mode 100644 jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/SimpleWebSocketTest.java create mode 100644 jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithAssertionsTest.java create mode 100644 jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithVariablesTest.java create mode 100644 jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/complex-websocket.jmx create mode 100644 jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/disabled-websocket-elements.jmx create mode 100644 jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-test-plan.template.jmx create mode 100644 jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-with-assertions.jmx create mode 100644 jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-with-variables.jmx diff --git a/docs/guide/protocols/websocket/index.md b/docs/guide/protocols/websocket/index.md new file mode 100644 index 00000000..0add822f --- /dev/null +++ b/docs/guide/protocols/websocket/index.md @@ -0,0 +1,210 @@ +# WebSocket Sampler Documentation + +## Overview + +The `DslWebsocketSampler` class provides a Java DSL for creating WebSocket performance tests using JMeter. It supports the full WebSocket lifecycle including connection, data transmission, and disconnection operations. + +## Features + +- **Connection Management**: Establish and close WebSocket connections +- **Data Transmission**: Send and receive WebSocket messages +- **URL Parsing**: Automatic parsing of WebSocket URLs (ws:// and wss://) +- **Timeout Configuration**: Configurable connection and response timeouts +- **TLS Support**: Secure WebSocket connections with WSS protocol +- **Fluent API**: Method chaining for easy configuration + +## Main Components + +### 1. DslWebsocketSampler (Main Class) + +The main class that provides static factory methods for creating different types of WebSocket samplers. + +#### Static Methods + +- `webSocketSampler()` - Creates a basic WebSocket sampler +- `connect()` - Creates a WebSocket connection sampler +- `connect(String url)` - Creates a WebSocket connection sampler with URL parsing +- `disconnect()` - Creates a WebSocket disconnection sampler +- `write()` - Creates a WebSocket write sampler +- `read()` - Creates a WebSocket read sampler + +### 2. DslConnectSampler (Connection Operations) + +Handles WebSocket connection establishment. + +#### Configuration Methods + +- `connectionTimeout(String timeout)` - Sets connection timeout in milliseconds +- `responseTimeout(String timeout)` - Sets response timeout in milliseconds +- `server(String server)` - Sets the WebSocket server hostname +- `port(String port)` - Sets the WebSocket server port +- `path(String path)` - Sets the WebSocket path +- `tls(boolean tls)` - Enables/disables TLS encryption + +#### URL Parsing + +The `connect(String url)` method automatically parses WebSocket URLs and extracts: +- Protocol (ws:// or wss://) +- Hostname +- Port (defaults to 80 for ws://, 443 for wss://) +- Path and query parameters +- TLS configuration + +**Supported URL formats:** +- `ws://localhost:8080/websocket` +- `wss://example.com:8443/chat?room=general` +- `wss://api.example.com/ws` + +### 3. DslDisconnectSampler (Disconnection Operations) + +Handles WebSocket connection closure. + +#### Configuration Methods + +- `responseTimeout(String timeout)` - Sets response timeout in milliseconds +- `statusCode(String statusCode)` - Sets the close status code (e.g., "1000" for normal closure) + +### 4. DslWriteSampler (Write Operations) + +Handles sending data through WebSocket connections. + +#### Configuration Methods + +- `connectionTimeout(String timeout)` - Sets connection timeout in milliseconds +- `requestData(String requestData)` - Sets the data to send +- `createNewConnection(boolean createNewConnection)` - Whether to create a new connection +- `loadDataFromFile(boolean loadDataFromFile)` - Whether to load data from a file + +### 5. DslReadSampler (Read Operations) + +Handles receiving data from WebSocket connections. + +#### Configuration Methods + +- `connectionTimeout(String timeout)` - Sets connection timeout in milliseconds +- `responseTimeout(String timeout)` - Sets response timeout in milliseconds +- `createNewConnection(boolean createNewConnection)` - Whether to create a new connection + +## Usage Examples + +### Basic WebSocket Test + +```java +import static us.abstracta.jmeter.javadsl.JmeterDsl.*; +import us.abstracta.jmeter.javadsl.websocket.DslWebsocketSampler; +import us.abstracta.jmeter.javadsl.core.TestPlanStats; + +public class Test { + public static void main(String[] args) throws Exception { + TestPlanStats stats = testPlan( + threadGroup(1, 1, + // Connect to WebSocket server + DslWebsocketSampler + .connect("wss://ws.postman-echo.com/raw") + .connectionTimeout("10000") + .responseTimeout("5000"), + + // Send a message + DslWebsocketSampler + .write() + .requestData("Hello WebSocket!") + .createNewConnection(false), + + // Read the response + DslWebsocketSampler + .read() + .responseTimeout("5000") + .createNewConnection(false) + .children( + responseAssertion() + .equalsToStrings("Hello WebSocket!") + ), + + // Close the connection + DslWebsocketSampler + .disconnect() + .responseTimeout("1000") + .statusCode("1000") + ) + ).run(); + } +} +``` + +### Manual Connection Configuration + +```java +DslWebsocketSampler + .connect() + .server("localhost") + .port("8080") + .path("/websocket") + .tls(false) + .connectionTimeout("5000") + .responseTimeout("3000") +``` + +### Connection with Assertions + +```java +DslWebsocketSampler + .read() + .responseTimeout("5000") + .createNewConnection(false) + .children( + responseAssertion() + .containsSubstrings("expected response") + ) +``` + +## Error Handling + +### Invalid URL Handling + +```java +// This will throw IllegalArgumentException +DslWebsocketSampler.connect("http://localhost:80/test"); +``` + +The URL parser validates: +- Protocol must be `ws://` or `wss://` +- Hostname is required +- Valid URI syntax + +### Connection Timeouts + +Configure appropriate timeouts to handle network issues: + +```java +DslWebsocketSampler + .connect("wss://example.com/ws") + .connectionTimeout("10000") // 10 seconds + .responseTimeout("5000") // 5 seconds +``` + +## Best Practices + +1. **Connection Reuse**: Set `createNewConnection(false)` for write/read operations to reuse existing connections +2. **Timeout Configuration**: Always set appropriate timeouts to avoid hanging tests +3. **Error Handling**: Use response assertions to validate WebSocket responses +4. **URL Parsing**: Use the `connect(String url)` method for cleaner code when you have complete URLs +5. **Status Codes**: Use standard WebSocket close codes (1000 for normal closure) + +## Integration with Test Plans + +WebSocket samplers integrate seamlessly with other JMeter DSL components: + +```java +testPlan( + threadGroup(10, 100, + // WebSocket operations + DslWebsocketSampler.connect("wss://api.example.com/ws"), + DslWebsocketSampler.write().requestData("test data"), + DslWebsocketSampler.read(), + DslWebsocketSampler.disconnect(), + ), + // Results collection + jtlWriter("results.jtl"), + resultsTreeVisualizer() +) +``` diff --git a/jmeter-java-dsl-cli/pom.xml b/jmeter-java-dsl-cli/pom.xml index e6a72064..edc2097b 100644 --- a/jmeter-java-dsl-cli/pom.xml +++ b/jmeter-java-dsl-cli/pom.xml @@ -201,7 +201,6 @@ org.apache-extras.beanshell:bsh com.jayway.jsonpath:json-path - org.jetbrains.kotlin:kotlin-stdlib org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm org.jetbrains.kotlinx:kotlinx-coroutines-swing diff --git a/jmeter-java-dsl-cli/src/test/java/us/abstracta/jmeter/javadsl/jmx2dsl/Jmx2DslIT.java b/jmeter-java-dsl-cli/src/test/java/us/abstracta/jmeter/javadsl/jmx2dsl/Jmx2DslIT.java index 0cba9a49..fa99aeb9 100644 --- a/jmeter-java-dsl-cli/src/test/java/us/abstracta/jmeter/javadsl/jmx2dsl/Jmx2DslIT.java +++ b/jmeter-java-dsl-cli/src/test/java/us/abstracta/jmeter/javadsl/jmx2dsl/Jmx2DslIT.java @@ -18,8 +18,8 @@ public class Jmx2DslIT { @Test public void shouldGetConvertedFileWhenConvert() throws Exception { Process p = startCommand("jmx2dsl", new TestResource("test-plan.jmx").filePath()); - assertThat(getProcessOutput(p)) - .isEqualTo(buildConvertedTestClass()); + assertThat(getProcessOutput(p).replace("\r\n", "\n")) + .isEqualTo(buildConvertedTestClass().replace("\r\n", "\n")); } private Process startCommand(String command, String... args) throws IOException { diff --git a/jmeter-java-dsl-websocket/pom.xml b/jmeter-java-dsl-websocket/pom.xml new file mode 100644 index 00000000..af4f3d0e --- /dev/null +++ b/jmeter-java-dsl-websocket/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + us.abstracta.jmeter + jmeter-java-dsl-parent + 1.30-SNAPSHOT + ../pom.xml + + + us.abstracta.jmeter.javadsl.websocket + jmeter-java-dsl-websocket + + + + us.abstracta.jmeter + jmeter-java-dsl + ${project.version} + + + net.luminis.jmeter + jmeter-websocket-samplers + 1.3.1 + + + org.apache.jmeter + ApacheJMeter_core + + + org.apache.jmeter + jorphan + + + + + + \ No newline at end of file diff --git a/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java b/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java new file mode 100644 index 00000000..f49b21fe --- /dev/null +++ b/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java @@ -0,0 +1,463 @@ +package us.abstracta.jmeter.javadsl.websocket; + +import eu.luminis.jmeter.wssampler.CloseWebSocketSampler; +import eu.luminis.jmeter.wssampler.CloseWebSocketSamplerGui; +import eu.luminis.jmeter.wssampler.DataPayloadType; +import eu.luminis.jmeter.wssampler.OpenWebSocketSampler; +import eu.luminis.jmeter.wssampler.OpenWebSocketSamplerGui; +import eu.luminis.jmeter.wssampler.SingleReadWebSocketSampler; +import eu.luminis.jmeter.wssampler.SingleReadWebSocketSampler.DataType; +import eu.luminis.jmeter.wssampler.SingleReadWebSocketSamplerGui; +import eu.luminis.jmeter.wssampler.SingleWriteWebSocketSampler; +import eu.luminis.jmeter.wssampler.SingleWriteWebSocketSamplerGui; +import java.lang.reflect.Method; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import org.apache.jmeter.testelement.TestElement; +import us.abstracta.jmeter.javadsl.codegeneration.MethodCall; +import us.abstracta.jmeter.javadsl.codegeneration.MethodCallContext; +import us.abstracta.jmeter.javadsl.codegeneration.MethodParam; +import us.abstracta.jmeter.javadsl.codegeneration.SingleTestElementCallBuilder; +import us.abstracta.jmeter.javadsl.codegeneration.TestElementParamBuilder; +import us.abstracta.jmeter.javadsl.codegeneration.params.StringParam; +import us.abstracta.jmeter.javadsl.core.samplers.BaseSampler; + +public class DslWebsocketSampler extends BaseSampler { + private static final String DEFAULT_NAME = "Websocket Sampler"; + + /** + * Creates a WebSocket sampler with URL parsing. + * + * @param name the name of the sampler + */ + protected DslWebsocketSampler(String name) { + super(name == null ? DEFAULT_NAME : name, OpenWebSocketSamplerGui.class); + } + + /** + * Creates a WebSocket sampler. + * + * @return the configured sampler + */ + public static DslWebsocketSampler webSocketSampler() { + return new DslWebsocketSampler("WebSocket Sampler"); + } + + /** + * Creates a WebSocket connect sampler. + * + * @return the connect sampler + */ + public static DslConnectSampler connect() { + return new DslConnectSampler(); + } + + /** + * Creates a WebSocket connect sampler with URL parsing. + * + * @param url the WebSocket URL to parse + * @return the connect sampler + */ + public static DslConnectSampler connect(String url) { + DslConnectSampler sampler = new DslConnectSampler(); + sampler.parseUrl(url); + return sampler; + } + + /** + * Creates a WebSocket disconnect sampler. + * + * @return the disconnect sampler + */ + public static DslDisconnectSampler disconnect() { + return new DslDisconnectSampler(); + } + + /** + * Creates a WebSocket write sampler. + * + * @return the write sampler + */ + public static DslWriteSampler write() { + return new DslWriteSampler(); + } + + /** + * Creates a WebSocket read sampler. + * + * @return the read sampler + */ + public static DslReadSampler read() { + return new DslReadSampler(); + } + + @Override + protected TestElement buildTestElement() { + throw new UnsupportedOperationException( + "Use specific sampler types: connect(), disconnect(), write(), or read()"); + } + + /** + * Inner class for WebSocket connect operations. + */ + public static class DslConnectSampler extends BaseSampler { + private String connectionTimeout; + private String responseTimeout; + private String server; + private String port; + private String path; + private boolean tls; + + public DslConnectSampler() { + super("WebSocket Open Connection", OpenWebSocketSamplerGui.class); + } + + /** + * Parses a WebSocket URL and sets the corresponding fields. + * + * @param url the WebSocket URL to parse + * @throws IllegalArgumentException if the URL is invalid + */ + private void parseUrl(String url) { + try { + URI uri = new URI(url); + + // Validate protocol + String scheme = uri.getScheme(); + if (scheme == null || (!"ws".equals(scheme) && !"wss".equals(scheme))) { + throw new IllegalArgumentException( + "Invalid WebSocket URL. Must start with 'ws://' or 'wss://'"); + } + + // Set TLS based on protocol + this.tls("wss".equals(scheme)); + + // Set server + this.server(uri.getHost()); + if (this.server == null) { + throw new IllegalArgumentException("Invalid WebSocket URL. Host is required"); + } + + // Set port + int port = uri.getPort(); + if (port == -1) { + // Default ports based on protocol + this.port(this.tls ? "443" : "80"); + } else { + this.port(String.valueOf(port)); + } + + // Set path + String path = uri.getPath(); + if (path == null || path.isEmpty()) { + this.path("/"); + } else { + this.path(path); + } + + // Add query string if present + String query = uri.getQuery(); + if (query != null && !query.isEmpty()) { + this.path(this.getPath() + "?" + query); + } + + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Invalid WebSocket URL: " + url, e); + } + } + + @Override + protected TestElement buildTestElement() { + OpenWebSocketSampler ret = new OpenWebSocketSampler(); + ret.setConnectTimeout(connectionTimeout); + ret.setReadTimeout(responseTimeout); + ret.setTLS(tls); + ret.setServer(server); + ret.setPort(port); + ret.setPath(path); + return ret; + } + + /** + * Sets the connection timeout for the WebSocket connection. + * + * @param timeout the connection timeout in milliseconds + * @return the sampler for further configuration or usage + */ + public DslConnectSampler connectionTimeout(String timeout) { + this.connectionTimeout = timeout; + return this; + } + + /** + * Sets the response timeout for the WebSocket operations. + * + * @param timeout the response timeout in milliseconds + * @return the sampler for further configuration or usage + */ + public DslConnectSampler responseTimeout(String timeout) { + this.responseTimeout = timeout; + return this; + } + + public DslConnectSampler server(String server) { + this.server = server; + return this; + } + + public DslConnectSampler port(String port) { + this.port = port; + return this; + } + + public DslConnectSampler path(String path) { + this.path = path; + return this; + } + + public String getPath() { + return path; + } + + public DslConnectSampler tls(boolean tls) { + this.tls = tls; + return this; + } + + public static class CodeBuilder extends SingleTestElementCallBuilder { + + public CodeBuilder(List builderMethods) { + super(OpenWebSocketSampler.class, builderMethods); + } + + @Override + protected MethodCall buildMethodCall(OpenWebSocketSampler testElement, + MethodCallContext context) { + TestElementParamBuilder paramBuilder = new TestElementParamBuilder(testElement); + MethodParam server = paramBuilder.stringParam("server"); + MethodParam port = paramBuilder.stringParam("port"); + MethodParam path = paramBuilder.stringParam("path"); + MethodParam tls = paramBuilder.boolParam("TLS", false); + + // Try to build URL if all required parameters are available + if (!server.isDefault() && !port.isDefault() && !path.isDefault()) { + String protocol = tls.isDefault() ? "ws" + : (tls.getExpression().equals("true") ? "wss" : "ws"); + String url = protocol + "://" + server.getExpression() + ":" + + port.getExpression() + path.getExpression(); + MethodCall ret = new MethodCall("DslWebsocketSampler.connect", + DslConnectSampler.class, new StringParam(url)); + + // Add non-default timeout parameters + MethodParam connectionTimeout = paramBuilder.stringParam("connectTimeout"); + if (!connectionTimeout.isDefault()) { + ret.chain("connectionTimeout", connectionTimeout); + } + MethodParam responseTimeout = paramBuilder.stringParam("readTimeout"); + if (!responseTimeout.isDefault()) { + ret.chain("responseTimeout", responseTimeout); + } + + return ret; + } else { + // Fall back to individual method calls + return buildMethodCall() + .chain("server", server) + .chain("port", port) + .chain("path", path) + .chain("tls", tls) + .chain("connectionTimeout", paramBuilder.stringParam("connectTimeout")) + .chain("responseTimeout", paramBuilder.stringParam("readTimeout")); + } + } + } + } + + /** + * Inner class for WebSocket disconnect operations. + */ + public static class DslDisconnectSampler extends BaseSampler { + private String responseTimeout; + private String statusCode; + + public DslDisconnectSampler() { + super("WebSocket Close", CloseWebSocketSamplerGui.class); + } + + @Override + protected TestElement buildTestElement() { + CloseWebSocketSampler close = new CloseWebSocketSampler(); + close.setReadTimeout(responseTimeout); + close.setStatusCode(statusCode); + return close; + } + + /** + * Sets the response timeout for the WebSocket operations. + * + * @param timeout the response timeout in milliseconds + * @return the sampler for further configuration or usage + */ + public DslDisconnectSampler responseTimeout(String timeout) { + this.responseTimeout = timeout; + return this; + } + + public DslDisconnectSampler statusCode(String statusCode) { + this.statusCode = statusCode; + return this; + } + + public static class CodeBuilder extends SingleTestElementCallBuilder { + + public CodeBuilder(List builderMethods) { + super(CloseWebSocketSampler.class, builderMethods); + } + + @Override + protected MethodCall buildMethodCall(CloseWebSocketSampler testElement, + MethodCallContext context) { + TestElementParamBuilder paramBuilder = new TestElementParamBuilder(testElement); + return new MethodCall("DslWebsocketSampler.disconnect", DslDisconnectSampler.class) + .chain("responseTimeout", paramBuilder.stringParam("readTimeout")) + .chain("statusCode", paramBuilder.stringParam("statusCode")); + } + } + } + + /** + * Inner class for WebSocket write operations. + */ + public static class DslWriteSampler extends BaseSampler { + private String connectionTimeout; + private String requestData; + private boolean createNewConnection = false; + private boolean loadDataFromFile = false; + + public DslWriteSampler() { + super("WebSocket Single Write", SingleWriteWebSocketSamplerGui.class); + } + + @Override + protected TestElement buildTestElement() { + SingleWriteWebSocketSampler write = new SingleWriteWebSocketSampler(); + write.setConnectTimeout(connectionTimeout != null ? connectionTimeout : "20000"); + write.setType(DataPayloadType.Text); + write.setRequestData(requestData); + write.setCreateNewConnection(createNewConnection); + return write; + } + + /** + * Sets the connection timeout for the WebSocket connection. + * + * @param timeout the connection timeout in milliseconds + * @return the sampler for further configuration or usage + */ + public DslWriteSampler connectionTimeout(String timeout) { + this.connectionTimeout = timeout; + return this; + } + + public DslWriteSampler requestData(String requestData) { + this.requestData = requestData; + return this; + } + + public DslWriteSampler loadDataFromFile(boolean loadDataFromFile) { + this.loadDataFromFile = loadDataFromFile; + return this; + } + + public DslWriteSampler createNewConnection(boolean createNewConnection) { + this.createNewConnection = createNewConnection; + return this; + } + + public static class CodeBuilder + extends SingleTestElementCallBuilder { + + public CodeBuilder(List builderMethods) { + super(SingleWriteWebSocketSampler.class, builderMethods); + } + + @Override + protected MethodCall buildMethodCall(SingleWriteWebSocketSampler testElement, + MethodCallContext context) { + TestElementParamBuilder paramBuilder = new TestElementParamBuilder(testElement); + return new MethodCall("DslWebsocketSampler.write", DslWriteSampler.class) + .chain("requestData", paramBuilder.stringParam("requestData")) + .chain("createNewConnection", paramBuilder.boolParam("createNewConnection", false)); + } + } + } + + /** + * Inner class for WebSocket read operations. + */ + public static class DslReadSampler extends BaseSampler { + private String connectionTimeout; + private String responseTimeout; + private boolean createNewConnection = false; + + public DslReadSampler() { + super("WebSocket Single Read", SingleReadWebSocketSamplerGui.class); + } + + @Override + protected TestElement buildTestElement() { + SingleReadWebSocketSampler read = new SingleReadWebSocketSampler(); + read.setConnectTimeout(connectionTimeout != null ? connectionTimeout : "20000"); + read.setReadTimeout(responseTimeout != null ? responseTimeout : "7000"); + read.setDataType(DataType.Text); + read.setOptional(false); + read.setCreateNewConnection(createNewConnection); + return read; + } + + /** + * Sets the connection timeout for the WebSocket connection. + * + * @param timeout the connection timeout in milliseconds + * @return the sampler for further configuration or usage + */ + public DslReadSampler connectionTimeout(String timeout) { + this.connectionTimeout = timeout; + return this; + } + + /** + * Sets the response timeout for the WebSocket operations. + * + * @param timeout the response timeout in milliseconds + * @return the sampler for further configuration or usage + */ + public DslReadSampler responseTimeout(String timeout) { + this.responseTimeout = timeout; + return this; + } + + public DslReadSampler createNewConnection(boolean createNewConnection) { + this.createNewConnection = createNewConnection; + return this; + } + + public static class CodeBuilder + extends SingleTestElementCallBuilder { + + public CodeBuilder(List builderMethods) { + super(SingleReadWebSocketSampler.class, builderMethods); + } + + @Override + protected MethodCall buildMethodCall(SingleReadWebSocketSampler testElement, + MethodCallContext context) { + TestElementParamBuilder paramBuilder = new TestElementParamBuilder(testElement); + return new MethodCall("DslWebsocketSampler.read", DslReadSampler.class) + .chain("responseTimeout", paramBuilder.stringParam("readTimeout")) + .chain("createNewConnection", paramBuilder.boolParam("createNewConnection", false)); + } + } + } +} diff --git a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketCodeGeneratorTest.java b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketCodeGeneratorTest.java new file mode 100644 index 00000000..2bdf7e94 --- /dev/null +++ b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketCodeGeneratorTest.java @@ -0,0 +1,86 @@ +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Set; +import org.apache.http.entity.ContentType; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import us.abstracta.jmeter.javadsl.codegeneration.DslCodeGenerator; +import us.abstracta.jmeter.javadsl.codegeneration.TestClassTemplate; +import us.abstracta.jmeter.javadsl.core.util.StringTemplate; +import us.abstracta.jmeter.javadsl.util.TestResource; +import us.abstracta.jmeter.javadsl.websocket.DslWebsocketSampler; + +public class DslWebsocketCodeGeneratorTest { + + private static final String RESOURCES_FOLDER = "websocket-codegeneration"; + + @Test + public void shouldGenerateExpectedCodeWhenSimpleWebSocketJmxIsProvided(@TempDir Path tempDir) + throws Exception { + File solvedTemplate = solveTemplateResource("websocket-test-plan.template.jmx", tempDir); + assertThat(new DslCodeGenerator().addBuildersFrom(DslWebsocketSampler.class).generateCodeFromJmx(solvedTemplate)) + .isEqualToNormalizingNewlines( + solveTestClassTemplate(Collections.emptySet(), + "SimpleWebSocketTest.java")); + } + + @Test + public void shouldGenerateExpectedCodeWhenComplexWebSocketJmxIsProvided(@TempDir Path tempDir) throws Exception { + File solvedTemplate = solveTemplateResource("/complex-websocket.jmx", tempDir); + assertThat(new DslCodeGenerator().addBuildersFrom(DslWebsocketSampler.class).generateCodeFromJmx(solvedTemplate)) + .isEqualToNormalizingNewlines(solveTestClassTemplate(Collections.emptySet(), + "ComplexWebSocketTest.java")); + } + + @Test + public void shouldGenerateExpectedCodeWhenWebSocketWithAssertionsJmxIsProvided(@TempDir Path tempDir) throws Exception { + File solvedTemplate = solveTemplateResource("/websocket-with-assertions.jmx", tempDir); + assertThat(new DslCodeGenerator().addBuildersFrom(DslWebsocketSampler.class).generateCodeFromJmx(solvedTemplate)) + .isEqualToNormalizingNewlines(solveTestClassTemplate(Collections.emptySet(), + "WebSocketWithAssertionsTest.java")); + } + + @Test + public void shouldGenerateExpectedCodeWhenWebSocketWithVariablesJmxIsProvided(@TempDir Path tempDir) throws Exception { + assertThat(new DslCodeGenerator() + .addBuildersFrom(DslWebsocketSampler.class) + .addDependency(DslWebsocketSampler.class, "us.abstracta.jmeter:jmeter-java-dsl-websocket") + .generateCodeFromJmx(new TestResource(RESOURCES_FOLDER + "/websocket-with-variables.jmx").file())) + .isEqualToNormalizingNewlines(solveTestClassTemplate(Collections.emptySet(), + "WebSocketWithVariablesTest.java")); + } + + @Test + public void shouldGenerateCommentedElementsCodeWhenDisabledWebSocketElementsInJmx(@TempDir Path tempDir) throws Exception { + assertThat(new DslCodeGenerator() + .addBuildersFrom(DslWebsocketSampler.class) + .addDependency(DslWebsocketSampler.class, "us.abstracta.jmeter:jmeter-java-dsl-websocket") + .generateCodeFromJmx(new TestResource(RESOURCES_FOLDER + "/disabled-websocket-elements.jmx").file())) + .isEqualToNormalizingNewlines(solveTestClassTemplate(Collections.emptySet(), + "DisabledWebSocketElements.java")); + } + + private File solveTemplateResource(String resourcePath, Path tempDir) throws IOException { + String templateContents = new StringTemplate(new TestResource(RESOURCES_FOLDER + "/" + resourcePath).rawContents()) + .solve(); + Path solvedTemplate = tempDir.resolve("websocket-test-plan.jmx"); + Files.write(solvedTemplate, templateContents.getBytes(StandardCharsets.UTF_8)); + return solvedTemplate.toFile(); + } + + private String solveTestClassTemplate(Set imports, String testPlanCodeResource) + throws IOException { + return new TestClassTemplate() + .dependencies(Collections.singleton("us.abstracta.jmeter:jmeter-java-dsl")) + .imports(imports) + .testPlan(new TestResource(RESOURCES_FOLDER + "/" + testPlanCodeResource).rawContents()) + .solve(); + } + +} diff --git a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java new file mode 100644 index 00000000..539c6bb1 --- /dev/null +++ b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java @@ -0,0 +1,217 @@ +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static us.abstracta.jmeter.javadsl.JmeterDsl.responseAssertion; +import static us.abstracta.jmeter.javadsl.JmeterDsl.testPlan; +import static us.abstracta.jmeter.javadsl.JmeterDsl.threadGroup; + +import org.junit.jupiter.api.*; + +import us.abstracta.jmeter.javadsl.core.TestPlanStats; +import us.abstracta.jmeter.javadsl.websocket.*; + +public class DslWebsocketSamplerTest { + + @Test + public void shouldConnectAndEchoMessageWhenWebSocketTestPlanWithEchoServer() throws Exception { + + TestPlanStats stats = testPlan( + threadGroup(1, 1, + DslWebsocketSampler + .connect("wss://ws.postman-echo.com/raw") + .connectionTimeout("10000") + .responseTimeout("5000"), + DslWebsocketSampler + .write() + .requestData("Hello WebSocket Test!") + .createNewConnection(false), + DslWebsocketSampler + .read() + .connectionTimeout("10000") + .createNewConnection(false) + .responseTimeout("5000") + .children( + responseAssertion() + .containsSubstrings("Hello WebSocket Test!") + ), + DslWebsocketSampler + .disconnect() + .responseTimeout("1000") + .statusCode("1000"))) + .run(); + assertThat(stats.overall().errorsCount()).isEqualTo(0); + } + + @Test + public void shouldThrowIllegalArgumentExceptionWhenConnectWithInvalidUrl() { + assertThrows(IllegalArgumentException.class, () -> { + DslWebsocketSampler.connect("http://localhost:80/test"); + }); + } + + @Test + public void shouldHandleConnectionFailureWhenConnectToUnavailableServer() throws Exception { + TestPlanStats stats = testPlan( + threadGroup(1, 1, + DslWebsocketSampler + .connect("ws://localhost:9999/nonexistent") + .connectionTimeout("2000") + .responseTimeout("1000"))) + .run(); + assertThat(stats.overall().errorsCount()).isEqualTo(1); + } + + @Test + public void shouldHandleTimeoutWhenConnectWithVeryShortTimeout() throws Exception { + TestPlanStats stats = testPlan( + threadGroup(1, 1, + DslWebsocketSampler + .connect("ws://localhost:8080/test") + .connectionTimeout("1") + .responseTimeout("1"))) + .run(); + assertThat(stats.overall().errorsCount()).isEqualTo(1); + } + + @Test + public void shouldHandleWriteOperationWhenNoPreviousConnection() throws Exception { + TestPlanStats stats = testPlan( + threadGroup(1, 1, + DslWebsocketSampler + .write() + .requestData("Test message") + .createNewConnection(false) + )).run(); + assertThat(stats.overall().errorsCount()).isEqualTo(1); + } + + @Test + public void shouldHandleReadOperationWhenNoPreviousConnection() throws Exception { + TestPlanStats stats = testPlan( + threadGroup(1, 1, + DslWebsocketSampler + .read() + .createNewConnection(false) + .responseTimeout("1000"))) + .run(); + assertThat(stats.overall().errorsCount()).isEqualTo(1); + } + + @Test + public void shouldHandleDisconnectOperationWhenNoPreviousConnection() throws Exception { + TestPlanStats stats = testPlan( + threadGroup(1, 1, + DslWebsocketSampler + .disconnect() + .responseTimeout("1000") + .statusCode("1000"))) + .run(); + assertThat(stats.overall().errorsCount()).isEqualTo(1); + } +} + + +// import eu.luminis.jmeter.wssampler.*; + +// import java.net.URISyntaxException; + +// import static org.junit.jupiter.api.Assertions.*; + +// class DslWebsocketSamplerTest { + +// @Test +// void shouldParseWsUrlCorrectly() { +// DslWebsocketSampler.DslConnectSampler sampler = DslWebsocketSampler.connect("ws://localhost:8080/chat"); + +// assertEquals("localhost", sampler.server); +// assertEquals("8080", sampler.port); +// assertEquals("/chat", sampler.getPath()); +// assertFalse(sampler.tls); +// } + +// @Test +// void shouldParseWssUrlWithDefaultPortAndQuery() { +// DslWebsocketSampler.DslConnectSampler sampler = DslWebsocketSampler.connect("wss://example.com/socket?token=abc"); + +// assertEquals("example.com", sampler.server); +// assertEquals("443", sampler.port); +// assertEquals("/socket?token=abc", sampler.getPath()); +// assertTrue(sampler.tls); +// } + +// @Test +// void shouldThrowExceptionWhenParsingInvalidUrl() { +// IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> { +// DslWebsocketSampler.connect("http://invalid.com"); +// }); + +// assertTrue(ex.getMessage().contains("Invalid WebSocket URL")); +// } + +// @Test +// void shouldBuildOpenWebSocketSamplerWithAllFields() { +// DslWebsocketSampler.DslConnectSampler sampler = new DslWebsocketSampler.DslConnectSampler() +// .server("localhost") +// .port("9000") +// .path("/ws") +// .tls(true) +// .connectionTimeout("10000") +// .responseTimeout("5000"); + +// OpenWebSocketSampler element = (OpenWebSocketSampler) sampler.buildTestElement(); + +// assertEquals("localhost", element.getServer()); +// assertEquals("9000", element.getPort()); +// assertEquals("/ws", element.getPath()); +// assertTrue(element.isTLS()); +// assertEquals("10000", element.getConnectTimeout()); +// assertEquals("5000", element.getReadTimeout()); +// } + +// @Test +// void shouldBuildCloseWebSocketSamplerWithAllFields() { +// DslWebsocketSampler.DslDisconnectSampler sampler = new DslWebsocketSampler.DslDisconnectSampler() +// .responseTimeout("7000") +// .statusCode("1001"); + +// CloseWebSocketSampler element = (CloseWebSocketSampler) sampler.buildTestElement(); + +// assertEquals("7000", element.getReadTimeout()); +// assertEquals("1001", element.getStatusCode()); +// } + +// @Test +// void shouldBuildWriteSamplerWithCustomValues() { +// DslWebsocketSampler.DslWriteSampler sampler = new DslWebsocketSampler.DslWriteSampler() +// .connectionTimeout("15000") +// .requestData("Hello WebSocket") +// .createNewConnection(true); + +// SingleWriteWebSocketSampler element = (SingleWriteWebSocketSampler) sampler.buildTestElement(); + +// assertEquals("15000", element.getConnectTimeout()); +// assertEquals("Hello WebSocket", element.getRequestData()); +// assertTrue(element.isCreateNewConnection()); +// } + +// @Test +// void shouldBuildReadSamplerWithCustomValues() { +// DslWebsocketSampler.DslReadSampler sampler = new DslWebsocketSampler.DslReadSampler() +// .connectionTimeout("12000") +// .responseTimeout("9000") +// .createNewConnection(true); + +// SingleReadWebSocketSampler element = (SingleReadWebSocketSampler) sampler.buildTestElement(); + +// assertEquals("12000", element.getConnectTimeout()); +// assertEquals("9000", element.getReadTimeout()); +// assertTrue(element.isCreateNewConnection()); +// assertEquals(SingleReadWebSocketSampler.DataType.Text, element.getDataType()); +// assertFalse(element.isOptional()); +// } + +// @Test +// void shouldThrowExceptionWhenUsingBaseWebsocketSamplerDirectly() { +// DslWebsocketSampler sampler = DslWebsocketSampler.webSocketSampler(); +// assertThrows(UnsupportedOperationException.class, sampler::buildTestElement); +// } +// } diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/ComplexWebSocketTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/ComplexWebSocketTest.java new file mode 100644 index 00000000..e7f07c11 --- /dev/null +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/ComplexWebSocketTest.java @@ -0,0 +1,14 @@ +testPlan( + threadGroup(1, 1, + DslWebsocketSampler.connect("ws://echo.websocket.org:80/") + .connectionTimeout("15000") + .responseTimeout("10000"), + DslWebsocketSampler.write() + .requestData("Hello from JMeter WebSocket Test"), + DslWebsocketSampler.read() + .responseTimeout("10000"), + DslWebsocketSampler.disconnect() + .responseTimeout("2000") + .statusCode("1000") + ) + ) \ No newline at end of file diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElements.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElements.java new file mode 100644 index 00000000..6879315d --- /dev/null +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElements.java @@ -0,0 +1,16 @@ +testPlan( + threadGroup(1, 1, + DslWebsocketSampler.connect("ws://ws.postman-echo.com:80/raw") + .connectionTimeout("10000") + .responseTimeout("5000"), + //DslWebsocketSampler.write() + // .requestData("Disabled write message"), + DslWebsocketSampler.read() + .responseTimeout("5000"), + //DslWebsocketSampler.write() + // .requestData("Another disabled write"), + DslWebsocketSampler.disconnect() + .responseTimeout("1000") + .statusCode("1000") + ) + ) \ No newline at end of file diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElementsTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElementsTest.java new file mode 100644 index 00000000..0f7fe5a6 --- /dev/null +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElementsTest.java @@ -0,0 +1,28 @@ + testPlan( + threadGroup(1, 1, + DslWebsocketSampler + .connect() + .server("ws.postman-echo.com") + .port("80") + .path("/raw") + .tls(false) + .connectionTimeout("10000") + .responseTimeout("5000"), + // DslWebsocketSampler + // .write() + // .requestData("Disabled write message") + // .createNewConnection(false), + DslWebsocketSampler + .read() + .responseTimeout("5000") + .createNewConnection(false), + // DslWebsocketSampler + // .write() + // .requestData("Another disabled write") + // .createNewConnection(false), + DslWebsocketSampler + .disconnect() + .responseTimeout("1000") + .statusCode("1000") + ) + ) diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/SimpleWebSocketTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/SimpleWebSocketTest.java new file mode 100644 index 00000000..30f37243 --- /dev/null +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/SimpleWebSocketTest.java @@ -0,0 +1,14 @@ +testPlan( + threadGroup(1, 1, + DslWebsocketSampler.connect("ws://ws.postman-echo.com:80/raw") + .connectionTimeout("10000") + .responseTimeout("5000"), + DslWebsocketSampler.write() + .requestData("Hello WebSocket Test!"), + DslWebsocketSampler.read() + .responseTimeout("5000"), + DslWebsocketSampler.disconnect() + .responseTimeout("1000") + .statusCode("1000") + ) + ) \ No newline at end of file diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithAssertionsTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithAssertionsTest.java new file mode 100644 index 00000000..4f3c53ae --- /dev/null +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithAssertionsTest.java @@ -0,0 +1,18 @@ +testPlan( + threadGroup(1, 1, + DslWebsocketSampler.connect("ws://ws.postman-echo.com:80/raw") + .connectionTimeout("10000") + .responseTimeout("5000"), + DslWebsocketSampler.write() + .requestData("Test message with assertions"), + DslWebsocketSampler.read() + .responseTimeout("5000") + .children( + responseAssertion() + .containsRegexes("Test message with assertions") + ), + DslWebsocketSampler.disconnect() + .responseTimeout("1000") + .statusCode("1000") + ) + ) \ No newline at end of file diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithVariablesTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithVariablesTest.java new file mode 100644 index 00000000..d1f0f82f --- /dev/null +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithVariablesTest.java @@ -0,0 +1,18 @@ +testPlan( + vars() + .set("WEBSOCKET_SERVER", "ws.postman-echo.com") + .set("WEBSOCKET_PORT", "80") + .set("MESSAGE", "Hello from variable"), + threadGroup(1, 1, + DslWebsocketSampler.connect("ws://${WEBSOCKET_SERVER}:${WEBSOCKET_PORT}/raw") + .connectionTimeout("10000") + .responseTimeout("5000"), + DslWebsocketSampler.write() + .requestData("${MESSAGE}"), + DslWebsocketSampler.read() + .responseTimeout("5000"), + DslWebsocketSampler.disconnect() + .responseTimeout("1000") + .statusCode("1000") + ) + ) \ No newline at end of file diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/complex-websocket.jmx b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/complex-websocket.jmx new file mode 100644 index 00000000..fc50f39d --- /dev/null +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/complex-websocket.jmx @@ -0,0 +1,68 @@ + + + + + + false + true + false + + + + + + + + 1 + 1 + true + continue + + 1 + false + + + + + 15000 + 10000 + false + echo.websocket.org + 80 + / + + + + 20000 + Text + Hello from JMeter WebSocket Test + false + false + + 80 + + false + + + + + 20000 + 10000 + Text + false + false + false + + 80 + + + + + 2000 + 1000 + + + + + + diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/disabled-websocket-elements.jmx b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/disabled-websocket-elements.jmx new file mode 100644 index 00000000..a3b06e7f --- /dev/null +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/disabled-websocket-elements.jmx @@ -0,0 +1,84 @@ + + + + + + false + true + false + + + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + 10000 + 5000 + false + ws.postman-echo.com + 80 + /raw + + + + 20000 + Text + Disabled write message + false + false + + 80 + + false + + + + + 20000 + 5000 + Text + false + false + false + + 80 + + + + + 20000 + Text + Another disabled write + false + false + + 80 + + false + + + + + 1000 + 1000 + + + + + + diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-test-plan.template.jmx b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-test-plan.template.jmx new file mode 100644 index 00000000..7ce05446 --- /dev/null +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-test-plan.template.jmx @@ -0,0 +1,71 @@ + + + + + + false + true + false + + + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + 10000 + 5000 + false + ws.postman-echo.com + 80 + /raw + + + + 20000 + Text + Hello WebSocket Test! + false + false + + 80 + + false + + + + + 20000 + 5000 + Text + false + false + false + + 80 + + + + + 1000 + 1000 + + + + + + diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-with-assertions.jmx b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-with-assertions.jmx new file mode 100644 index 00000000..0fb1f417 --- /dev/null +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-with-assertions.jmx @@ -0,0 +1,82 @@ + + + + + + false + true + false + + + + + + + + continue + + false + 1 + + 1 + 1 + false + + + true + + + + 10000 + 5000 + false + ws.postman-echo.com + 80 + /raw + + + + 20000 + Text + Test message with assertions + false + false + + 80 + + false + + + + + 20000 + 5000 + Text + false + false + false + + 80 + + + + + + Test message with assertions + + + Assertion.response_data + false + 2 + + + + + 1000 + 1000 + + + + + + diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-with-variables.jmx b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-with-variables.jmx new file mode 100644 index 00000000..332d7b25 --- /dev/null +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-with-variables.jmx @@ -0,0 +1,100 @@ + + + + + true + + + + WEBSOCKET_SERVER + ws.postman-echo.com + = + + + WEBSOCKET_PORT + 80 + = + + + MESSAGE + Hello from variable + = + + + + + + + + + WEBSOCKET_SERVER + ws.postman-echo.com + = + + + WEBSOCKET_PORT + 80 + = + + + MESSAGE + Hello from variable + = + + + + + + 1 + 1 + true + continue + + 1 + false + + + + + 10000 + 5000 + false + ${WEBSOCKET_SERVER} + ${WEBSOCKET_PORT} + /raw + + + + 20000 + Text + ${MESSAGE} + false + false + + 80 + + false + + + + + 20000 + 5000 + Text + false + false + false + + 80 + + + + + 1000 + 1000 + + + + + + diff --git a/pom.xml b/pom.xml index 105ad5fa..ee9121e4 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,7 @@ jmeter-java-dsl-datadog jmeter-java-dsl-bridge jmeter-java-dsl-prometheus + jmeter-java-dsl-websocket From 65f538ab207502cdd17433c258ad275ad4497708 Mon Sep 17 00:00:00 2001 From: fdelpoggio Date: Tue, 2 Dec 2025 11:06:29 -0300 Subject: [PATCH 03/11] Fix: update websocket module parent POM version to 2.2-SNAPSHOT and remove commented code --- jmeter-java-dsl-websocket/pom.xml | 2 +- .../java/DslWebsocketCodeGeneratorTest.java | 1 - .../test/java/DslWebsocketSamplerTest.java | 109 +----------------- 3 files changed, 2 insertions(+), 110 deletions(-) diff --git a/jmeter-java-dsl-websocket/pom.xml b/jmeter-java-dsl-websocket/pom.xml index af4f3d0e..9580ee3b 100644 --- a/jmeter-java-dsl-websocket/pom.xml +++ b/jmeter-java-dsl-websocket/pom.xml @@ -6,7 +6,7 @@ us.abstracta.jmeter jmeter-java-dsl-parent - 1.30-SNAPSHOT + 2.2-SNAPSHOT ../pom.xml diff --git a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketCodeGeneratorTest.java b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketCodeGeneratorTest.java index 2bdf7e94..72bcc52e 100644 --- a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketCodeGeneratorTest.java +++ b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketCodeGeneratorTest.java @@ -7,7 +7,6 @@ import java.nio.file.Path; import java.util.Collections; import java.util.Set; -import org.apache.http.entity.ContentType; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import us.abstracta.jmeter.javadsl.codegeneration.DslCodeGenerator; diff --git a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java index 539c6bb1..7dbf0377 100644 --- a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java +++ b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java @@ -107,111 +107,4 @@ public void shouldHandleDisconnectOperationWhenNoPreviousConnection() throws Exc .run(); assertThat(stats.overall().errorsCount()).isEqualTo(1); } -} - - -// import eu.luminis.jmeter.wssampler.*; - -// import java.net.URISyntaxException; - -// import static org.junit.jupiter.api.Assertions.*; - -// class DslWebsocketSamplerTest { - -// @Test -// void shouldParseWsUrlCorrectly() { -// DslWebsocketSampler.DslConnectSampler sampler = DslWebsocketSampler.connect("ws://localhost:8080/chat"); - -// assertEquals("localhost", sampler.server); -// assertEquals("8080", sampler.port); -// assertEquals("/chat", sampler.getPath()); -// assertFalse(sampler.tls); -// } - -// @Test -// void shouldParseWssUrlWithDefaultPortAndQuery() { -// DslWebsocketSampler.DslConnectSampler sampler = DslWebsocketSampler.connect("wss://example.com/socket?token=abc"); - -// assertEquals("example.com", sampler.server); -// assertEquals("443", sampler.port); -// assertEquals("/socket?token=abc", sampler.getPath()); -// assertTrue(sampler.tls); -// } - -// @Test -// void shouldThrowExceptionWhenParsingInvalidUrl() { -// IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> { -// DslWebsocketSampler.connect("http://invalid.com"); -// }); - -// assertTrue(ex.getMessage().contains("Invalid WebSocket URL")); -// } - -// @Test -// void shouldBuildOpenWebSocketSamplerWithAllFields() { -// DslWebsocketSampler.DslConnectSampler sampler = new DslWebsocketSampler.DslConnectSampler() -// .server("localhost") -// .port("9000") -// .path("/ws") -// .tls(true) -// .connectionTimeout("10000") -// .responseTimeout("5000"); - -// OpenWebSocketSampler element = (OpenWebSocketSampler) sampler.buildTestElement(); - -// assertEquals("localhost", element.getServer()); -// assertEquals("9000", element.getPort()); -// assertEquals("/ws", element.getPath()); -// assertTrue(element.isTLS()); -// assertEquals("10000", element.getConnectTimeout()); -// assertEquals("5000", element.getReadTimeout()); -// } - -// @Test -// void shouldBuildCloseWebSocketSamplerWithAllFields() { -// DslWebsocketSampler.DslDisconnectSampler sampler = new DslWebsocketSampler.DslDisconnectSampler() -// .responseTimeout("7000") -// .statusCode("1001"); - -// CloseWebSocketSampler element = (CloseWebSocketSampler) sampler.buildTestElement(); - -// assertEquals("7000", element.getReadTimeout()); -// assertEquals("1001", element.getStatusCode()); -// } - -// @Test -// void shouldBuildWriteSamplerWithCustomValues() { -// DslWebsocketSampler.DslWriteSampler sampler = new DslWebsocketSampler.DslWriteSampler() -// .connectionTimeout("15000") -// .requestData("Hello WebSocket") -// .createNewConnection(true); - -// SingleWriteWebSocketSampler element = (SingleWriteWebSocketSampler) sampler.buildTestElement(); - -// assertEquals("15000", element.getConnectTimeout()); -// assertEquals("Hello WebSocket", element.getRequestData()); -// assertTrue(element.isCreateNewConnection()); -// } - -// @Test -// void shouldBuildReadSamplerWithCustomValues() { -// DslWebsocketSampler.DslReadSampler sampler = new DslWebsocketSampler.DslReadSampler() -// .connectionTimeout("12000") -// .responseTimeout("9000") -// .createNewConnection(true); - -// SingleReadWebSocketSampler element = (SingleReadWebSocketSampler) sampler.buildTestElement(); - -// assertEquals("12000", element.getConnectTimeout()); -// assertEquals("9000", element.getReadTimeout()); -// assertTrue(element.isCreateNewConnection()); -// assertEquals(SingleReadWebSocketSampler.DataType.Text, element.getDataType()); -// assertFalse(element.isOptional()); -// } - -// @Test -// void shouldThrowExceptionWhenUsingBaseWebsocketSamplerDirectly() { -// DslWebsocketSampler sampler = DslWebsocketSampler.webSocketSampler(); -// assertThrows(UnsupportedOperationException.class, sampler::buildTestElement); -// } -// } +} \ No newline at end of file From 94c2da39818eb781f2972bc93d2972d6ef49cc2a Mon Sep 17 00:00:00 2001 From: Sebastian Lorenzo Date: Wed, 3 Dec 2025 11:30:01 -0300 Subject: [PATCH 04/11] Refactors on web socket sampler --- docs/guide/protocols/index.md | 1 + .../{websocket/index.md => websocket.md} | 109 ++++++------- jmeter-java-dsl-cli/pom.xml | 6 + .../jmeter/javadsl/cli/Jmx2DslCommand.java | 2 + jmeter-java-dsl-websocket/pom.xml | 1 - .../websocket/DslWebsocketSampler.java | 143 ++++++++++++------ .../test/java/DslWebsocketSamplerTest.java | 38 +++-- .../ComplexWebSocketTest.java | 8 +- .../DisabledWebSocketElements.java | 10 +- .../DisabledWebSocketElementsTest.java | 10 +- .../SimpleWebSocketTest.java | 8 +- .../WebSocketWithAssertionsTest.java | 8 +- .../WebSocketWithVariablesTest.java | 8 +- 13 files changed, 195 insertions(+), 157 deletions(-) rename docs/guide/protocols/{websocket/index.md => websocket.md} (65%) diff --git a/docs/guide/protocols/index.md b/docs/guide/protocols/index.md index 479eccd0..d4d1edca 100644 --- a/docs/guide/protocols/index.md +++ b/docs/guide/protocols/index.md @@ -5,3 +5,4 @@ + diff --git a/docs/guide/protocols/websocket/index.md b/docs/guide/protocols/websocket.md similarity index 65% rename from docs/guide/protocols/websocket/index.md rename to docs/guide/protocols/websocket.md index 0add822f..827e3bd5 100644 --- a/docs/guide/protocols/websocket/index.md +++ b/docs/guide/protocols/websocket.md @@ -1,38 +1,27 @@ -# WebSocket Sampler Documentation +### WebSocket Sampler Documentation -## Overview +The `DslWebsocketSampler` class provides a Java DSL for creating WebSocket performance tests using JMeter. It supports the full WebSocket lifecycle including connection, data transmission, and disconnection operations. It is based on [WebSocket Samplers by Peter Doornbosch](https://bitbucket.org/pjtr/jmeter-websocket-samplers/src/master/) plugin. -The `DslWebsocketSampler` class provides a Java DSL for creating WebSocket performance tests using JMeter. It supports the full WebSocket lifecycle including connection, data transmission, and disconnection operations. +To use it, add the following dependency to your project: -## Features - -- **Connection Management**: Establish and close WebSocket connections -- **Data Transmission**: Send and receive WebSocket messages -- **URL Parsing**: Automatic parsing of WebSocket URLs (ws:// and wss://) -- **Timeout Configuration**: Configurable connection and response timeouts -- **TLS Support**: Secure WebSocket connections with WSS protocol -- **Fluent API**: Method chaining for easy configuration - -## Main Components - -### 1. DslWebsocketSampler (Main Class) - -The main class that provides static factory methods for creating different types of WebSocket samplers. - -#### Static Methods - -- `webSocketSampler()` - Creates a basic WebSocket sampler -- `connect()` - Creates a WebSocket connection sampler -- `connect(String url)` - Creates a WebSocket connection sampler with URL parsing -- `disconnect()` - Creates a WebSocket disconnection sampler -- `write()` - Creates a WebSocket write sampler -- `read()` - Creates a WebSocket read sampler +```xml + + us.abstracta.jmeter + jmeter-java-dsl-websocket + 2.2 + test + +``` -### 2. DslConnectSampler (Connection Operations) +#### Main Components -Handles WebSocket connection establishment. +- `webSocketSampler().connect()` - Creates a WebSocket connection sampler +- `webSocketSampler().connect(String url)` - Creates a WebSocket connection sampler with URL parsing +- `webSocketSampler().disconnect()` - Creates a WebSocket disconnection sampler +- `webSocketSampler().write()` - Creates a WebSocket write sampler +- `webSocketSampler().read()` - Creates a WebSocket read sampler -#### Configuration Methods +##### Connect configuration - `connectionTimeout(String timeout)` - Sets connection timeout in milliseconds - `responseTimeout(String timeout)` - Sets response timeout in milliseconds @@ -41,7 +30,7 @@ Handles WebSocket connection establishment. - `path(String path)` - Sets the WebSocket path - `tls(boolean tls)` - Enables/disables TLS encryption -#### URL Parsing +##### URL Parsing The `connect(String url)` method automatically parses WebSocket URLs and extracts: - Protocol (ws:// or wss://) @@ -55,39 +44,27 @@ The `connect(String url)` method automatically parses WebSocket URLs and extract - `wss://example.com:8443/chat?room=general` - `wss://api.example.com/ws` -### 3. DslDisconnectSampler (Disconnection Operations) - -Handles WebSocket connection closure. - -#### Configuration Methods +##### Disconnection configuration - `responseTimeout(String timeout)` - Sets response timeout in milliseconds - `statusCode(String statusCode)` - Sets the close status code (e.g., "1000" for normal closure) -### 4. DslWriteSampler (Write Operations) - -Handles sending data through WebSocket connections. - -#### Configuration Methods +##### Write configuration - `connectionTimeout(String timeout)` - Sets connection timeout in milliseconds - `requestData(String requestData)` - Sets the data to send - `createNewConnection(boolean createNewConnection)` - Whether to create a new connection - `loadDataFromFile(boolean loadDataFromFile)` - Whether to load data from a file -### 5. DslReadSampler (Read Operations) - -Handles receiving data from WebSocket connections. - -#### Configuration Methods +##### Read configuration - `connectionTimeout(String timeout)` - Sets connection timeout in milliseconds - `responseTimeout(String timeout)` - Sets response timeout in milliseconds - `createNewConnection(boolean createNewConnection)` - Whether to create a new connection -## Usage Examples +#### Usage Examples -### Basic WebSocket Test +##### Basic WebSocket Test ```java import static us.abstracta.jmeter.javadsl.JmeterDsl.*; @@ -99,19 +76,19 @@ public class Test { TestPlanStats stats = testPlan( threadGroup(1, 1, // Connect to WebSocket server - DslWebsocketSampler + webSocketSampler() .connect("wss://ws.postman-echo.com/raw") .connectionTimeout("10000") .responseTimeout("5000"), // Send a message - DslWebsocketSampler + webSocketSampler() .write() .requestData("Hello WebSocket!") .createNewConnection(false), // Read the response - DslWebsocketSampler + webSocketSampler() .read() .responseTimeout("5000") .createNewConnection(false) @@ -121,7 +98,7 @@ public class Test { ), // Close the connection - DslWebsocketSampler + webSocketSampler() .disconnect() .responseTimeout("1000") .statusCode("1000") @@ -131,10 +108,10 @@ public class Test { } ``` -### Manual Connection Configuration +##### Manual Connection Configuration ```java -DslWebsocketSampler +webSocketSampler() .connect() .server("localhost") .port("8080") @@ -144,10 +121,10 @@ DslWebsocketSampler .responseTimeout("3000") ``` -### Connection with Assertions +##### Connection with Assertions ```java -DslWebsocketSampler +webSocketSampler() .read() .responseTimeout("5000") .createNewConnection(false) @@ -157,13 +134,13 @@ DslWebsocketSampler ) ``` -## Error Handling +#### Error Handling -### Invalid URL Handling +##### Invalid URL Handling ```java // This will throw IllegalArgumentException -DslWebsocketSampler.connect("http://localhost:80/test"); +webSocketSampler().connect("http://localhost:80/test"); ``` The URL parser validates: @@ -171,18 +148,18 @@ The URL parser validates: - Hostname is required - Valid URI syntax -### Connection Timeouts +##### Connection Timeouts Configure appropriate timeouts to handle network issues: ```java -DslWebsocketSampler +webSocketSampler() .connect("wss://example.com/ws") .connectionTimeout("10000") // 10 seconds .responseTimeout("5000") // 5 seconds ``` -## Best Practices +#### Best Practices 1. **Connection Reuse**: Set `createNewConnection(false)` for write/read operations to reuse existing connections 2. **Timeout Configuration**: Always set appropriate timeouts to avoid hanging tests @@ -190,7 +167,7 @@ DslWebsocketSampler 4. **URL Parsing**: Use the `connect(String url)` method for cleaner code when you have complete URLs 5. **Status Codes**: Use standard WebSocket close codes (1000 for normal closure) -## Integration with Test Plans +#### Integration with Test Plans WebSocket samplers integrate seamlessly with other JMeter DSL components: @@ -198,10 +175,10 @@ WebSocket samplers integrate seamlessly with other JMeter DSL components: testPlan( threadGroup(10, 100, // WebSocket operations - DslWebsocketSampler.connect("wss://api.example.com/ws"), - DslWebsocketSampler.write().requestData("test data"), - DslWebsocketSampler.read(), - DslWebsocketSampler.disconnect(), + webSocketSampler().connect("wss://api.example.com/ws"), + webSocketSampler().write().requestData("test data"), + webSocketSampler().read(), + webSocketSampler().disconnect(), ), // Results collection jtlWriter("results.jtl"), diff --git a/jmeter-java-dsl-cli/pom.xml b/jmeter-java-dsl-cli/pom.xml index edc2097b..d7f2114f 100644 --- a/jmeter-java-dsl-cli/pom.xml +++ b/jmeter-java-dsl-cli/pom.xml @@ -65,6 +65,11 @@ jmeter-java-dsl-recorder ${project.version} + + us.abstracta.jmeter + jmeter-java-dsl-websocket + ${project.version} + org.slf4j jul-to-slf4j @@ -201,6 +206,7 @@ org.apache-extras.beanshell:bsh com.jayway.jsonpath:json-path + org.jetbrains.kotlin:kotlin-stdlib org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm org.jetbrains.kotlinx:kotlinx-coroutines-swing diff --git a/jmeter-java-dsl-cli/src/main/java/us/abstracta/jmeter/javadsl/cli/Jmx2DslCommand.java b/jmeter-java-dsl-cli/src/main/java/us/abstracta/jmeter/javadsl/cli/Jmx2DslCommand.java index c92f27a2..5b6e7edb 100644 --- a/jmeter-java-dsl-cli/src/main/java/us/abstracta/jmeter/javadsl/cli/Jmx2DslCommand.java +++ b/jmeter-java-dsl-cli/src/main/java/us/abstracta/jmeter/javadsl/cli/Jmx2DslCommand.java @@ -11,6 +11,7 @@ import us.abstracta.jmeter.javadsl.graphql.DslGraphqlSampler; import us.abstracta.jmeter.javadsl.jdbc.JdbcJmeterDsl; import us.abstracta.jmeter.javadsl.parallel.ParallelController; +import us.abstracta.jmeter.javadsl.websocket.DslWebsocketSampler; import us.abstracta.jmeter.javadsl.wrapper.WrapperJmeterDsl; @Command(name = "jmx2dsl", header = "Converts a JMX file to DSL code", @@ -48,6 +49,7 @@ public Integer call() throws Exception { addBuildersFrom(ElasticsearchBackendListener.class, "jmeter-java-dsl-elasticsearch-listener", codeGenerator); addBuildersFrom(DatadogBackendListener.class, "jmeter-java-dsl-datadog", codeGenerator); + addBuildersFrom(DslWebsocketSampler.class, "jmeter-java-dsl-websocket", codeGenerator); System.out.println(codeGenerator.generateCodeFromJmx(jmxFile)); return 0; } diff --git a/jmeter-java-dsl-websocket/pom.xml b/jmeter-java-dsl-websocket/pom.xml index 9580ee3b..a889883e 100644 --- a/jmeter-java-dsl-websocket/pom.xml +++ b/jmeter-java-dsl-websocket/pom.xml @@ -10,7 +10,6 @@ ../pom.xml - us.abstracta.jmeter.javadsl.websocket jmeter-java-dsl-websocket diff --git a/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java b/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java index f49b21fe..a2ab9d3a 100644 --- a/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java +++ b/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java @@ -23,31 +23,41 @@ import us.abstracta.jmeter.javadsl.codegeneration.params.StringParam; import us.abstracta.jmeter.javadsl.core.samplers.BaseSampler; +/** + * Allows to create a WebSocket sampler to establish a connection, send a + * message, read a response and disconnect. + * + * @since 2.2 + */ public class DslWebsocketSampler extends BaseSampler { private static final String DEFAULT_NAME = "Websocket Sampler"; /** - * Creates a WebSocket sampler with URL parsing. + * Creates a WebSocket sampler. * * @param name the name of the sampler + * @return the sampler for further configuration or usage + * @since 2.2 */ - protected DslWebsocketSampler(String name) { + private DslWebsocketSampler(String name) { super(name == null ? DEFAULT_NAME : name, OpenWebSocketSamplerGui.class); } /** * Creates a WebSocket sampler. * - * @return the configured sampler + * @return the sampler for further configuration or usage + * @since 2.2 */ public static DslWebsocketSampler webSocketSampler() { - return new DslWebsocketSampler("WebSocket Sampler"); + return new DslWebsocketSampler(DEFAULT_NAME); } /** * Creates a WebSocket connect sampler. * - * @return the connect sampler + * @return the connect sampler for further configuration or usage + * @since 2.2 */ public static DslConnectSampler connect() { return new DslConnectSampler(); @@ -56,8 +66,9 @@ public static DslConnectSampler connect() { /** * Creates a WebSocket connect sampler with URL parsing. * - * @param url the WebSocket URL to parse - * @return the connect sampler + * @param url the WebSocket server URL + * @return the connect sampler for further configuration or usage + * @since 2.2 */ public static DslConnectSampler connect(String url) { DslConnectSampler sampler = new DslConnectSampler(); @@ -68,7 +79,8 @@ public static DslConnectSampler connect(String url) { /** * Creates a WebSocket disconnect sampler. * - * @return the disconnect sampler + * @return the disconnect sampler for further configuration or usage + * @since 2.2 */ public static DslDisconnectSampler disconnect() { return new DslDisconnectSampler(); @@ -77,7 +89,8 @@ public static DslDisconnectSampler disconnect() { /** * Creates a WebSocket write sampler. * - * @return the write sampler + * @return the write sampler for further configuration or usage + * @since 2.2 */ public static DslWriteSampler write() { return new DslWriteSampler(); @@ -86,7 +99,8 @@ public static DslWriteSampler write() { /** * Creates a WebSocket read sampler. * - * @return the read sampler + * @return the read sampler for further configuration or usage + * @since 2.2 */ public static DslReadSampler read() { return new DslReadSampler(); @@ -109,7 +123,7 @@ public static class DslConnectSampler extends BaseSampler { private String path; private boolean tls; - public DslConnectSampler() { + private DslConnectSampler() { super("WebSocket Open Connection", OpenWebSocketSamplerGui.class); } @@ -123,32 +137,25 @@ private void parseUrl(String url) { try { URI uri = new URI(url); - // Validate protocol String scheme = uri.getScheme(); if (scheme == null || (!"ws".equals(scheme) && !"wss".equals(scheme))) { throw new IllegalArgumentException( "Invalid WebSocket URL. Must start with 'ws://' or 'wss://'"); } - // Set TLS based on protocol this.tls("wss".equals(scheme)); - - // Set server this.server(uri.getHost()); if (this.server == null) { throw new IllegalArgumentException("Invalid WebSocket URL. Host is required"); } - // Set port int port = uri.getPort(); if (port == -1) { - // Default ports based on protocol this.port(this.tls ? "443" : "80"); } else { this.port(String.valueOf(port)); } - // Set path String path = uri.getPath(); if (path == null || path.isEmpty()) { this.path("/"); @@ -156,7 +163,6 @@ private void parseUrl(String url) { this.path(path); } - // Add query string if present String query = uri.getQuery(); if (query != null && !query.isEmpty()) { this.path(this.getPath() + "?" + query); @@ -184,6 +190,7 @@ protected TestElement buildTestElement() { * * @param timeout the connection timeout in milliseconds * @return the sampler for further configuration or usage + * @since 2.2 */ public DslConnectSampler connectionTimeout(String timeout) { this.connectionTimeout = timeout; @@ -195,31 +202,60 @@ public DslConnectSampler connectionTimeout(String timeout) { * * @param timeout the response timeout in milliseconds * @return the sampler for further configuration or usage + * @since 2.2 */ public DslConnectSampler responseTimeout(String timeout) { this.responseTimeout = timeout; return this; } + /** + * Specifies the WebSocket server to connect to. + * + * @param server the WebSocket server to connect to + * @return the sampler for further configuration or usage + * @since 2.2 + */ public DslConnectSampler server(String server) { this.server = server; return this; } + /** + * Sets the WebSocket port to connect to. + * + * @param port the WebSocket port to connect to + * @return the sampler for further configuration or usage + * @since 2.2 + */ public DslConnectSampler port(String port) { this.port = port; return this; } + /** + * Sets the WebSocket path to connect to. + * + * @param path the WebSocket path to connect to + * @return the sampler for further configuration or usage + * @since 2.2 + */ public DslConnectSampler path(String path) { this.path = path; return this; } - public String getPath() { + private String getPath() { return path; } + /** + * Sets the WebSocket TLS flag. + * + * @param tls the WebSocket TLS flag + * @return the sampler for further configuration or usage + * @since 2.2 + */ public DslConnectSampler tls(boolean tls) { this.tls = tls; return this; @@ -232,23 +268,23 @@ public CodeBuilder(List builderMethods) { } @Override - protected MethodCall buildMethodCall(OpenWebSocketSampler testElement, + protected MethodCall buildMethodCall(OpenWebSocketSampler testElement, MethodCallContext context) { TestElementParamBuilder paramBuilder = new TestElementParamBuilder(testElement); MethodParam server = paramBuilder.stringParam("server"); MethodParam port = paramBuilder.stringParam("port"); MethodParam path = paramBuilder.stringParam("path"); MethodParam tls = paramBuilder.boolParam("TLS", false); - + // Try to build URL if all required parameters are available if (!server.isDefault() && !port.isDefault() && !path.isDefault()) { - String protocol = tls.isDefault() ? "ws" + String protocol = tls.isDefault() ? "ws" : (tls.getExpression().equals("true") ? "wss" : "ws"); - String url = protocol + "://" + server.getExpression() + ":" + String url = protocol + "://" + server.getExpression() + ":" + port.getExpression() + path.getExpression(); - MethodCall ret = new MethodCall("DslWebsocketSampler.connect", + MethodCall ret = new MethodCall("webSocketSampler().connect", DslConnectSampler.class, new StringParam(url)); - + // Add non-default timeout parameters MethodParam connectionTimeout = paramBuilder.stringParam("connectTimeout"); if (!connectionTimeout.isDefault()) { @@ -258,11 +294,12 @@ protected MethodCall buildMethodCall(OpenWebSocketSampler testElement, if (!responseTimeout.isDefault()) { ret.chain("responseTimeout", responseTimeout); } - + return ret; } else { // Fall back to individual method calls return buildMethodCall() + .chain("webSocketSampler().connect()") .chain("server", server) .chain("port", port) .chain("path", path) @@ -281,7 +318,7 @@ public static class DslDisconnectSampler extends BaseSampler builderMethods) { } @Override - protected MethodCall buildMethodCall(CloseWebSocketSampler testElement, + protected MethodCall buildMethodCall(CloseWebSocketSampler testElement, MethodCallContext context) { TestElementParamBuilder paramBuilder = new TestElementParamBuilder(testElement); - return new MethodCall("DslWebsocketSampler.disconnect", DslDisconnectSampler.class) + return new MethodCall("webSocketSampler().disconnect", DslDisconnectSampler.class) .chain("responseTimeout", paramBuilder.stringParam("readTimeout")) .chain("statusCode", paramBuilder.stringParam("statusCode")); } @@ -333,9 +378,8 @@ public static class DslWriteSampler extends BaseSampler { private String connectionTimeout; private String requestData; private boolean createNewConnection = false; - private boolean loadDataFromFile = false; - public DslWriteSampler() { + private DslWriteSampler() { super("WebSocket Single Write", SingleWriteWebSocketSamplerGui.class); } @@ -354,28 +398,38 @@ protected TestElement buildTestElement() { * * @param timeout the connection timeout in milliseconds * @return the sampler for further configuration or usage + * @since 2.2 */ public DslWriteSampler connectionTimeout(String timeout) { this.connectionTimeout = timeout; return this; } + /** + * Sets the request data for the WebSocket write. + * + * @param requestData the request data for the WebSocket write + * @return the sampler for further configuration or usage + * @since 2.2 + */ public DslWriteSampler requestData(String requestData) { this.requestData = requestData; return this; } - public DslWriteSampler loadDataFromFile(boolean loadDataFromFile) { - this.loadDataFromFile = loadDataFromFile; - return this; - } - + /** + * Sets the create new connection flag for the WebSocket write. + * + * @param createNewConnection the create new connection flag for the WebSocket write + * @return the sampler for further configuration or usage + * @since 2.2 + */ public DslWriteSampler createNewConnection(boolean createNewConnection) { this.createNewConnection = createNewConnection; return this; } - public static class CodeBuilder + public static class CodeBuilder extends SingleTestElementCallBuilder { public CodeBuilder(List builderMethods) { @@ -383,10 +437,10 @@ public CodeBuilder(List builderMethods) { } @Override - protected MethodCall buildMethodCall(SingleWriteWebSocketSampler testElement, + protected MethodCall buildMethodCall(SingleWriteWebSocketSampler testElement, MethodCallContext context) { TestElementParamBuilder paramBuilder = new TestElementParamBuilder(testElement); - return new MethodCall("DslWebsocketSampler.write", DslWriteSampler.class) + return new MethodCall("webSocketSampler().write", DslWriteSampler.class) .chain("requestData", paramBuilder.stringParam("requestData")) .chain("createNewConnection", paramBuilder.boolParam("createNewConnection", false)); } @@ -401,7 +455,7 @@ public static class DslReadSampler extends BaseSampler { private String responseTimeout; private boolean createNewConnection = false; - public DslReadSampler() { + private DslReadSampler() { super("WebSocket Single Read", SingleReadWebSocketSamplerGui.class); } @@ -421,6 +475,7 @@ protected TestElement buildTestElement() { * * @param timeout the connection timeout in milliseconds * @return the sampler for further configuration or usage + * @since 2.2 */ public DslReadSampler connectionTimeout(String timeout) { this.connectionTimeout = timeout; @@ -443,7 +498,7 @@ public DslReadSampler createNewConnection(boolean createNewConnection) { return this; } - public static class CodeBuilder + public static class CodeBuilder extends SingleTestElementCallBuilder { public CodeBuilder(List builderMethods) { @@ -451,10 +506,10 @@ public CodeBuilder(List builderMethods) { } @Override - protected MethodCall buildMethodCall(SingleReadWebSocketSampler testElement, + protected MethodCall buildMethodCall(SingleReadWebSocketSampler testElement, MethodCallContext context) { TestElementParamBuilder paramBuilder = new TestElementParamBuilder(testElement); - return new MethodCall("DslWebsocketSampler.read", DslReadSampler.class) + return new MethodCall("webSocketSampler().read", DslReadSampler.class) .chain("responseTimeout", paramBuilder.stringParam("readTimeout")) .chain("createNewConnection", paramBuilder.boolParam("createNewConnection", false)); } diff --git a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java index 7dbf0377..8eaf18f0 100644 --- a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java +++ b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java @@ -1,13 +1,12 @@ -import static org.assertj.core.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; import static us.abstracta.jmeter.javadsl.JmeterDsl.responseAssertion; import static us.abstracta.jmeter.javadsl.JmeterDsl.testPlan; import static us.abstracta.jmeter.javadsl.JmeterDsl.threadGroup; +import static us.abstracta.jmeter.javadsl.websocket.DslWebsocketSampler.webSocketSampler; -import org.junit.jupiter.api.*; - +import org.junit.jupiter.api.Test; import us.abstracta.jmeter.javadsl.core.TestPlanStats; -import us.abstracta.jmeter.javadsl.websocket.*; public class DslWebsocketSamplerTest { @@ -16,35 +15,34 @@ public void shouldConnectAndEchoMessageWhenWebSocketTestPlanWithEchoServer() thr TestPlanStats stats = testPlan( threadGroup(1, 1, - DslWebsocketSampler + webSocketSampler() .connect("wss://ws.postman-echo.com/raw") .connectionTimeout("10000") .responseTimeout("5000"), - DslWebsocketSampler + webSocketSampler() .write() .requestData("Hello WebSocket Test!") .createNewConnection(false), - DslWebsocketSampler + webSocketSampler() .read() .connectionTimeout("10000") .createNewConnection(false) .responseTimeout("5000") .children( responseAssertion() - .containsSubstrings("Hello WebSocket Test!") - ), - DslWebsocketSampler + .containsSubstrings("Hello WebSocket Test!")), + webSocketSampler() .disconnect() .responseTimeout("1000") .statusCode("1000"))) .run(); assertThat(stats.overall().errorsCount()).isEqualTo(0); } - + @Test public void shouldThrowIllegalArgumentExceptionWhenConnectWithInvalidUrl() { assertThrows(IllegalArgumentException.class, () -> { - DslWebsocketSampler.connect("http://localhost:80/test"); + webSocketSampler().connect("http://localhost:80/test"); }); } @@ -52,7 +50,7 @@ public void shouldThrowIllegalArgumentExceptionWhenConnectWithInvalidUrl() { public void shouldHandleConnectionFailureWhenConnectToUnavailableServer() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - DslWebsocketSampler + webSocketSampler() .connect("ws://localhost:9999/nonexistent") .connectionTimeout("2000") .responseTimeout("1000"))) @@ -64,7 +62,7 @@ public void shouldHandleConnectionFailureWhenConnectToUnavailableServer() throws public void shouldHandleTimeoutWhenConnectWithVeryShortTimeout() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - DslWebsocketSampler + webSocketSampler() .connect("ws://localhost:8080/test") .connectionTimeout("1") .responseTimeout("1"))) @@ -76,11 +74,11 @@ public void shouldHandleTimeoutWhenConnectWithVeryShortTimeout() throws Exceptio public void shouldHandleWriteOperationWhenNoPreviousConnection() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - DslWebsocketSampler + webSocketSampler() .write() .requestData("Test message") - .createNewConnection(false) - )).run(); + .createNewConnection(false))) + .run(); assertThat(stats.overall().errorsCount()).isEqualTo(1); } @@ -88,7 +86,7 @@ public void shouldHandleWriteOperationWhenNoPreviousConnection() throws Exceptio public void shouldHandleReadOperationWhenNoPreviousConnection() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - DslWebsocketSampler + webSocketSampler() .read() .createNewConnection(false) .responseTimeout("1000"))) @@ -100,7 +98,7 @@ public void shouldHandleReadOperationWhenNoPreviousConnection() throws Exception public void shouldHandleDisconnectOperationWhenNoPreviousConnection() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - DslWebsocketSampler + webSocketSampler() .disconnect() .responseTimeout("1000") .statusCode("1000"))) diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/ComplexWebSocketTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/ComplexWebSocketTest.java index e7f07c11..8330ba5d 100644 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/ComplexWebSocketTest.java +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/ComplexWebSocketTest.java @@ -1,13 +1,13 @@ testPlan( threadGroup(1, 1, - DslWebsocketSampler.connect("ws://echo.websocket.org:80/") + webSocketSampler().connect("ws://echo.websocket.org:80/") .connectionTimeout("15000") .responseTimeout("10000"), - DslWebsocketSampler.write() + webSocketSampler().write() .requestData("Hello from JMeter WebSocket Test"), - DslWebsocketSampler.read() + webSocketSampler().read() .responseTimeout("10000"), - DslWebsocketSampler.disconnect() + webSocketSampler().disconnect() .responseTimeout("2000") .statusCode("1000") ) diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElements.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElements.java index 6879315d..47652a11 100644 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElements.java +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElements.java @@ -1,15 +1,15 @@ testPlan( threadGroup(1, 1, - DslWebsocketSampler.connect("ws://ws.postman-echo.com:80/raw") + webSocketSampler().connect("ws://ws.postman-echo.com:80/raw") .connectionTimeout("10000") .responseTimeout("5000"), - //DslWebsocketSampler.write() + //webSocketSampler().write() // .requestData("Disabled write message"), - DslWebsocketSampler.read() + webSocketSampler().read() .responseTimeout("5000"), - //DslWebsocketSampler.write() + //webSocketSampler().write() // .requestData("Another disabled write"), - DslWebsocketSampler.disconnect() + webSocketSampler().disconnect() .responseTimeout("1000") .statusCode("1000") ) diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElementsTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElementsTest.java index 0f7fe5a6..e571d79e 100644 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElementsTest.java +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElementsTest.java @@ -1,6 +1,6 @@ testPlan( threadGroup(1, 1, - DslWebsocketSampler + webSocketSampler() .connect() .server("ws.postman-echo.com") .port("80") @@ -8,19 +8,19 @@ .tls(false) .connectionTimeout("10000") .responseTimeout("5000"), - // DslWebsocketSampler + // webSocketSampler() // .write() // .requestData("Disabled write message") // .createNewConnection(false), - DslWebsocketSampler + webSocketSampler() .read() .responseTimeout("5000") .createNewConnection(false), - // DslWebsocketSampler + // webSocketSampler() // .write() // .requestData("Another disabled write") // .createNewConnection(false), - DslWebsocketSampler + webSocketSampler() .disconnect() .responseTimeout("1000") .statusCode("1000") diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/SimpleWebSocketTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/SimpleWebSocketTest.java index 30f37243..2384ef75 100644 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/SimpleWebSocketTest.java +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/SimpleWebSocketTest.java @@ -1,13 +1,13 @@ testPlan( threadGroup(1, 1, - DslWebsocketSampler.connect("ws://ws.postman-echo.com:80/raw") + webSocketSampler().connect("ws://ws.postman-echo.com:80/raw") .connectionTimeout("10000") .responseTimeout("5000"), - DslWebsocketSampler.write() + webSocketSampler().write() .requestData("Hello WebSocket Test!"), - DslWebsocketSampler.read() + webSocketSampler().read() .responseTimeout("5000"), - DslWebsocketSampler.disconnect() + webSocketSampler().disconnect() .responseTimeout("1000") .statusCode("1000") ) diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithAssertionsTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithAssertionsTest.java index 4f3c53ae..3396b495 100644 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithAssertionsTest.java +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithAssertionsTest.java @@ -1,17 +1,17 @@ testPlan( threadGroup(1, 1, - DslWebsocketSampler.connect("ws://ws.postman-echo.com:80/raw") + webSocketSampler().connect("ws://ws.postman-echo.com:80/raw") .connectionTimeout("10000") .responseTimeout("5000"), - DslWebsocketSampler.write() + webSocketSampler().write() .requestData("Test message with assertions"), - DslWebsocketSampler.read() + webSocketSampler().read() .responseTimeout("5000") .children( responseAssertion() .containsRegexes("Test message with assertions") ), - DslWebsocketSampler.disconnect() + webSocketSampler().disconnect() .responseTimeout("1000") .statusCode("1000") ) diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithVariablesTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithVariablesTest.java index d1f0f82f..29f055f1 100644 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithVariablesTest.java +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithVariablesTest.java @@ -4,14 +4,14 @@ .set("WEBSOCKET_PORT", "80") .set("MESSAGE", "Hello from variable"), threadGroup(1, 1, - DslWebsocketSampler.connect("ws://${WEBSOCKET_SERVER}:${WEBSOCKET_PORT}/raw") + webSocketSampler().connect("ws://${WEBSOCKET_SERVER}:${WEBSOCKET_PORT}/raw") .connectionTimeout("10000") .responseTimeout("5000"), - DslWebsocketSampler.write() + webSocketSampler().write() .requestData("${MESSAGE}"), - DslWebsocketSampler.read() + webSocketSampler().read() .responseTimeout("5000"), - DslWebsocketSampler.disconnect() + webSocketSampler().disconnect() .responseTimeout("1000") .statusCode("1000") ) From 4d3cca1f3cb14d52a9a2ae0eb730bd7c3aac891b Mon Sep 17 00:00:00 2001 From: Sebastian Lorenzo Date: Wed, 3 Dec 2025 12:17:10 -0300 Subject: [PATCH 05/11] Add missing web socket dependency to cli --- jmeter-java-dsl-cli/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/jmeter-java-dsl-cli/pom.xml b/jmeter-java-dsl-cli/pom.xml index d7f2114f..300c1d40 100644 --- a/jmeter-java-dsl-cli/pom.xml +++ b/jmeter-java-dsl-cli/pom.xml @@ -170,6 +170,7 @@ com.blazemeter:jmeter-plugins-random-csv-data-set com.blazemeter:jmeter-plugins-wsc com.blazemeter:jmeter-parallel + net.luminis.jmeter:jmeter-websocket-samplers info.picocli:* com.fifesoft:rsyntaxtextarea com.github.weisj:darklaf-extensions-rsyntaxarea From 51a077d4c3fbcbb30e735ae3270af415e07257fd Mon Sep 17 00:00:00 2001 From: Sebastian Lorenzo Date: Wed, 3 Dec 2025 15:57:19 -0300 Subject: [PATCH 06/11] Fix issue with Octoperf engine due change of api --- .../javadsl/octoperf/OctoPerfEngine.java | 8 +- .../jmeter/javadsl/octoperf/api/Scenario.java | 20 +---- .../api/{UserLoad.java => UserProfile.java} | 85 ++++++++++++------- .../javadsl/octoperf/OctoPerfEngineTest.java | 2 - 4 files changed, 64 insertions(+), 51 deletions(-) rename jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/{UserLoad.java => UserProfile.java} (62%) diff --git a/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/OctoPerfEngine.java b/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/OctoPerfEngine.java index 195158e6..d9782d20 100644 --- a/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/OctoPerfEngine.java +++ b/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/OctoPerfEngine.java @@ -31,8 +31,8 @@ import us.abstracta.jmeter.javadsl.octoperf.api.Scenario; import us.abstracta.jmeter.javadsl.octoperf.api.TableEntry; import us.abstracta.jmeter.javadsl.octoperf.api.User; -import us.abstracta.jmeter.javadsl.octoperf.api.UserLoad; -import us.abstracta.jmeter.javadsl.octoperf.api.UserLoad.UserLoadRampUp; +import us.abstracta.jmeter.javadsl.octoperf.api.UserProfile; +import us.abstracta.jmeter.javadsl.octoperf.api.UserProfile.UserLoadRampUp; import us.abstracta.jmeter.javadsl.octoperf.api.VirtualUser; import us.abstracta.jmeter.javadsl.octoperf.api.Workspace; @@ -264,8 +264,8 @@ private Scenario buildScenario(User user, Project project, List vus HashTree tree) throws IOException { Provider provider = apiClient.findProviderByWorkspace(project.getWorkspace()); String defaultRegion = provider.getRegions().keySet().iterator().next(); - List userLoads = vus.stream() - .map(vu -> new UserLoad(vu.getId(), provider.getId(), defaultRegion, + List userLoads = vus.stream() + .map(vu -> new UserProfile(vu.getId(), provider.getId(), defaultRegion, buildUserLoadConfig(tree))) .collect(Collectors.toList()); Scenario ret = apiClient.createScenario( diff --git a/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/Scenario.java b/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/Scenario.java index bb8ea60c..5f7ceb8c 100644 --- a/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/Scenario.java +++ b/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/Scenario.java @@ -16,12 +16,11 @@ public class Scenario { private final String projectId; private final String name; private final String description = ""; - private final List userLoads; + private final List userProfiles; private final String mode = "STANDARD"; private final Instant created = Instant.now(); private final Instant lastModified = Instant.now(); private final Set tags; - private final BackendListenerSettings backendListeners = new BackendListenerSettings(); private Project project; @JsonCreator @@ -30,18 +29,18 @@ public Scenario(@JsonProperty("id") String id, @JsonProperty("tags") Set this.userId = null; this.projectId = null; this.name = null; - this.userLoads = null; + this.userProfiles = null; this.tags = tags; } - public Scenario(User user, Project project, String name, List userLoads, + public Scenario(User user, Project project, String name, List userProfiles, Set tags) { this.id = ""; this.userId = user.getId(); this.project = project; this.projectId = project.getId(); this.name = name; - this.userLoads = userLoads; + this.userProfiles = userProfiles; this.tags = tags; } @@ -67,15 +66,4 @@ public String getUrl() { return project.getBaseUrl() + "/runtime/scenario/" + id; } - public static class BackendListenerSettings { - - private final int queueSize = 5000; - private final List listeners = Collections.emptyList(); - - } - - public static class BackendListener { - - } - } diff --git a/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/UserLoad.java b/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/UserProfile.java similarity index 62% rename from jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/UserLoad.java rename to jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/UserProfile.java index 24de9b74..a9331be5 100644 --- a/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/UserLoad.java +++ b/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/UserProfile.java @@ -6,43 +6,37 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeName; + import java.util.Collections; import java.util.List; import java.util.Map; import org.apache.jmeter.threads.ThreadGroup; import us.abstracta.jmeter.javadsl.core.threadgroups.BaseThreadGroup.SampleErrorAction; -public class UserLoad { +public class UserProfile { // we don't need getters since Jackson gets the values from fields private final String name = ""; private final String virtualUserId; private final String providerId; - private final String region; - private final UserLoadStrategy strategy; - private final BandwidthSettings bandwidth = new BandwidthSettings(); - private final BrowserSettings browser = new BrowserSettings(); - private final DnsSettings dns = new DnsSettings(); - private final ThinkTimeSettings thinktime = new ThinkTimeSettings(); + private final String location; + private final UserLoadStrategy load; private final MemorySettings memory = new MemorySettings(); - private final JtlSettings jtl = new JtlSettings(); - private final PropertiesSettings properties = new PropertiesSettings(); - private final SetUpTearDownSettings setUp = null; - private final SetUpTearDownSettings tearDown = null; + private final Engine engine = new Engine(); - public UserLoad() { + public UserProfile() { virtualUserId = null; providerId = null; - region = null; - strategy = null; + location = null; + load = null; } - public UserLoad(String virtualUserId, String providerId, String region, + public UserProfile(String virtualUserId, String providerId, String location, UserLoadStrategy strategy) { this.virtualUserId = virtualUserId; this.providerId = providerId; - this.region = region; - this.strategy = strategy; + this.location = location; + this.load = strategy; } @JsonTypeInfo(use = NAME, include = PROPERTY) @@ -53,25 +47,24 @@ public abstract static class UserLoadStrategy { } - @JsonTypeName("UserLoadRampup") + @JsonTypeName("UserProfileLoadRampUp") public static class UserLoadRampUp extends UserLoadStrategy { - private final int userload; - private final long rampup; - private final long peak; - private final long delay = 0; - private final SampleErrorAction onSampleError = SampleErrorAction.CONTINUE; + private final int plateauVus; + private final long rampUpMs; + private final long plateauMs; + private final long delayMs = 0; public UserLoadRampUp() { - userload = 0; - rampup = 0; - peak = 0; + plateauVus = 0; + rampUpMs = 0; + plateauMs = 0; } public UserLoadRampUp(int userLoad, long rampUpMillis, long peakMillis) { - this.userload = userLoad; - this.rampup = rampUpMillis; - this.peak = peakMillis; + this.plateauVus = userLoad; + this.rampUpMs = rampUpMillis; + this.plateauMs = peakMillis; } public static UserLoadRampUp fromThreadGroup(ThreadGroup threadGroup) { @@ -85,6 +78,38 @@ public static UserLoadRampUp fromThreadGroup(ThreadGroup threadGroup) { } + @JsonTypeInfo(use = NAME, include = PROPERTY) + @JsonTypeName("JmeterUserProfileEngine") + public static class Engine { + + private final EngineSettings settings = new EngineSettings(); + private final BrowserSettings browser = new BrowserSettings(); + private final BandwidthSettings bandwidth = new BandwidthSettings(); + private final DnsSettings dns = new DnsSettings(); + private final JtlSettings jtl = new JtlSettings(); + private final PropertiesSettings properties = new PropertiesSettings(); + + } + + public static class EngineSettings { + + private final ExternalLiveReportingSettings externalLiveReporting = new ExternalLiveReportingSettings(); + private final SampleErrorAction errorHandling = SampleErrorAction.CONTINUE; + private final ThinkTimeSettings thinkTime = new ThinkTimeSettings(); + private final SetUpTearDownSettings setUp = null; + private final SetUpTearDownSettings tearDown = null; + + } + + @JsonTypeInfo(use = NAME, include = PROPERTY) + @JsonTypeName("JmeterExternalLiveReportingSettings") + public static class ExternalLiveReportingSettings { + + private final List listeners = Collections.emptyList(); + private final int queueSize = 5000; + + } + public static class SetUpTearDownSettings { } @@ -102,6 +127,8 @@ public static class BrowserSettings { private final String userAgent = ""; private final CacheManager cache = new CacheManager(); private final CookiesManager cookies = new CookiesManager(); + private final Boolean downloadResources = null; + private final Boolean keepAlive = null; } diff --git a/jmeter-java-dsl-octoperf/src/test/java/us/abstracta/jmeter/javadsl/octoperf/OctoPerfEngineTest.java b/jmeter-java-dsl-octoperf/src/test/java/us/abstracta/jmeter/javadsl/octoperf/OctoPerfEngineTest.java index 54d4ae52..502c44b8 100644 --- a/jmeter-java-dsl-octoperf/src/test/java/us/abstracta/jmeter/javadsl/octoperf/OctoPerfEngineTest.java +++ b/jmeter-java-dsl-octoperf/src/test/java/us/abstracta/jmeter/javadsl/octoperf/OctoPerfEngineTest.java @@ -6,8 +6,6 @@ import static us.abstracta.jmeter.javadsl.JmeterDsl.threadGroup; import java.time.Duration; -import java.util.HashMap; -import java.util.Map; import org.junit.jupiter.api.Test; import us.abstracta.jmeter.javadsl.core.TestPlanStats; From 6b30de44dd09a6790c54bd40adbdf088a1adfe1c Mon Sep 17 00:00:00 2001 From: Sebastian Lorenzo Date: Wed, 3 Dec 2025 16:18:38 -0300 Subject: [PATCH 07/11] Fix code style --- .../us/abstracta/jmeter/javadsl/octoperf/api/Scenario.java | 1 - .../us/abstracta/jmeter/javadsl/octoperf/api/UserProfile.java | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/Scenario.java b/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/Scenario.java index 5f7ceb8c..93b9c3ea 100644 --- a/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/Scenario.java +++ b/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/Scenario.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import java.time.Instant; -import java.util.Collections; import java.util.List; import java.util.Set; diff --git a/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/UserProfile.java b/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/UserProfile.java index a9331be5..77c62d0c 100644 --- a/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/UserProfile.java +++ b/jmeter-java-dsl-octoperf/src/main/java/us/abstracta/jmeter/javadsl/octoperf/api/UserProfile.java @@ -6,7 +6,6 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeName; - import java.util.Collections; import java.util.List; import java.util.Map; @@ -93,7 +92,8 @@ public static class Engine { public static class EngineSettings { - private final ExternalLiveReportingSettings externalLiveReporting = new ExternalLiveReportingSettings(); + private final ExternalLiveReportingSettings externalLiveReporting = + new ExternalLiveReportingSettings(); private final SampleErrorAction errorHandling = SampleErrorAction.CONTINUE; private final ThinkTimeSettings thinkTime = new ThinkTimeSettings(); private final SetUpTearDownSettings setUp = null; From e50be8e7f59b32adc4d1865b751b8defc9ac46f5 Mon Sep 17 00:00:00 2001 From: Sebastian Lorenzo Date: Mon, 8 Dec 2025 17:06:39 -0300 Subject: [PATCH 08/11] Some refactors and update web socket documentation --- .gitattributes | 1 + docs/guide/protocols/websocket.md | 173 ++------ .../jmeter/javadsl/jmx2dsl/Jmx2DslIT.java | 4 +- .../websocket/DslWebsocketSampler.java | 395 ++++++++++++------ .../java/DslWebsocketCodeGeneratorTest.java | 10 - .../test/java/DslWebsocketSamplerTest.java | 19 +- .../DisabledWebSocketElements.java | 16 - .../DisabledWebSocketElementsTest.java | 28 -- .../disabled-websocket-elements.jmx | 84 ---- .../codegeneration/MethodCallBuilderTest.java | 2 +- .../javadsl/core/StringTemplateAssert.java | 2 +- 11 files changed, 302 insertions(+), 432 deletions(-) create mode 100644 .gitattributes delete mode 100644 jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElements.java delete mode 100644 jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElementsTest.java delete mode 100644 jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/disabled-websocket-elements.jmx diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..94f480de --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file diff --git a/docs/guide/protocols/websocket.md b/docs/guide/protocols/websocket.md index 827e3bd5..fa60863f 100644 --- a/docs/guide/protocols/websocket.md +++ b/docs/guide/protocols/websocket.md @@ -1,9 +1,11 @@ -### WebSocket Sampler Documentation +### WebSocket The `DslWebsocketSampler` class provides a Java DSL for creating WebSocket performance tests using JMeter. It supports the full WebSocket lifecycle including connection, data transmission, and disconnection operations. It is based on [WebSocket Samplers by Peter Doornbosch](https://bitbucket.org/pjtr/jmeter-websocket-samplers/src/master/) plugin. To use it, add the following dependency to your project: +:::: code-group +::: code-group-item Maven ```xml us.abstracta.jmeter @@ -12,176 +14,55 @@ To use it, add the following dependency to your project: test ``` +::: +::: code-group-item Gradle +```groovy +testImplementation 'us.abstracta.jmeter:jmeter-java-dsl-websocket:2.2' +``` +::: +:::: -#### Main Components - -- `webSocketSampler().connect()` - Creates a WebSocket connection sampler -- `webSocketSampler().connect(String url)` - Creates a WebSocket connection sampler with URL parsing -- `webSocketSampler().disconnect()` - Creates a WebSocket disconnection sampler -- `webSocketSampler().write()` - Creates a WebSocket write sampler -- `webSocketSampler().read()` - Creates a WebSocket read sampler - -##### Connect configuration - -- `connectionTimeout(String timeout)` - Sets connection timeout in milliseconds -- `responseTimeout(String timeout)` - Sets response timeout in milliseconds -- `server(String server)` - Sets the WebSocket server hostname -- `port(String port)` - Sets the WebSocket server port -- `path(String path)` - Sets the WebSocket path -- `tls(boolean tls)` - Enables/disables TLS encryption - -##### URL Parsing - -The `connect(String url)` method automatically parses WebSocket URLs and extracts: -- Protocol (ws:// or wss://) -- Hostname -- Port (defaults to 80 for ws://, 443 for wss://) -- Path and query parameters -- TLS configuration - -**Supported URL formats:** -- `ws://localhost:8080/websocket` -- `wss://example.com:8443/chat?room=general` -- `wss://api.example.com/ws` - -##### Disconnection configuration - -- `responseTimeout(String timeout)` - Sets response timeout in milliseconds -- `statusCode(String statusCode)` - Sets the close status code (e.g., "1000" for normal closure) - -##### Write configuration - -- `connectionTimeout(String timeout)` - Sets connection timeout in milliseconds -- `requestData(String requestData)` - Sets the data to send -- `createNewConnection(boolean createNewConnection)` - Whether to create a new connection -- `loadDataFromFile(boolean loadDataFromFile)` - Whether to load data from a file - -##### Read configuration - -- `connectionTimeout(String timeout)` - Sets connection timeout in milliseconds -- `responseTimeout(String timeout)` - Sets response timeout in milliseconds -- `createNewConnection(boolean createNewConnection)` - Whether to create a new connection -#### Usage Examples -##### Basic WebSocket Test +Following you can see a basic usage example of Web Socket protocol. ```java import static us.abstracta.jmeter.javadsl.JmeterDsl.*; -import us.abstracta.jmeter.javadsl.websocket.DslWebsocketSampler; +import static us.abstracta.jmeter.javadsl.websocket.DslWebsocketSampler.webSocketSampler; import us.abstracta.jmeter.javadsl.core.TestPlanStats; public class Test { public static void main(String[] args) throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - // Connect to WebSocket server - webSocketSampler() - .connect("wss://ws.postman-echo.com/raw") - .connectionTimeout("10000") - .responseTimeout("5000"), - - // Send a message - webSocketSampler() - .write() - .requestData("Hello WebSocket!") - .createNewConnection(false), - - // Read the response - webSocketSampler() - .read() - .responseTimeout("5000") - .createNewConnection(false) + webSocketSampler().connect("wss://ws.postman-echo.com/raw"), + webSocketSampler().write("Hello WebSocket!"), + webSocketSampler().read() .children( responseAssertion() .equalsToStrings("Hello WebSocket!") ), - - // Close the connection - webSocketSampler() - .disconnect() - .responseTimeout("1000") - .statusCode("1000") + webSocketSampler().disconnect() ) ).run(); } } ``` -##### Manual Connection Configuration - -```java -webSocketSampler() - .connect() - .server("localhost") - .port("8080") - .path("/websocket") - .tls(false) - .connectionTimeout("5000") - .responseTimeout("3000") -``` - -##### Connection with Assertions - -```java -webSocketSampler() - .read() - .responseTimeout("5000") - .createNewConnection(false) - .children( - responseAssertion() - .containsSubstrings("expected response") - ) -``` - -#### Error Handling - -##### Invalid URL Handling - -```java -// This will throw IllegalArgumentException -webSocketSampler().connect("http://localhost:80/test"); -``` - -The URL parser validates: -- Protocol must be `ws://` or `wss://` -- Hostname is required -- Valid URI syntax +::: warning +Only `ws://` and `wss://` protocols are supported. Using any other scheme will throw an `IllegalArgumentException`. +::: -##### Connection Timeouts - -Configure appropriate timeouts to handle network issues: +You can use a non blocking read if it is necessary in the following way ```java -webSocketSampler() - .connect("wss://example.com/ws") - .connectionTimeout("10000") // 10 seconds - .responseTimeout("5000") // 5 seconds +webSocketSampler().read().waitForResponse(false) ``` -#### Best Practices - -1. **Connection Reuse**: Set `createNewConnection(false)` for write/read operations to reuse existing connections -2. **Timeout Configuration**: Always set appropriate timeouts to avoid hanging tests -3. **Error Handling**: Use response assertions to validate WebSocket responses -4. **URL Parsing**: Use the `connect(String url)` method for cleaner code when you have complete URLs -5. **Status Codes**: Use standard WebSocket close codes (1000 for normal closure) +::: warning +In this case is not recommended to add an assertion due the response could be empty +::: -#### Integration with Test Plans - -WebSocket samplers integrate seamlessly with other JMeter DSL components: - -```java -testPlan( - threadGroup(10, 100, - // WebSocket operations - webSocketSampler().connect("wss://api.example.com/ws"), - webSocketSampler().write().requestData("test data"), - webSocketSampler().read(), - webSocketSampler().disconnect(), - ), - // Results collection - jtlWriter("results.jtl"), - resultsTreeVisualizer() -) -``` +::: tip +Web Socket protocol only supports one connection at a time. If you want to change Web Socket server during execution you should add a disconnect sampler and then establish a new connection. +::: \ No newline at end of file diff --git a/jmeter-java-dsl-cli/src/test/java/us/abstracta/jmeter/javadsl/jmx2dsl/Jmx2DslIT.java b/jmeter-java-dsl-cli/src/test/java/us/abstracta/jmeter/javadsl/jmx2dsl/Jmx2DslIT.java index fa99aeb9..0cba9a49 100644 --- a/jmeter-java-dsl-cli/src/test/java/us/abstracta/jmeter/javadsl/jmx2dsl/Jmx2DslIT.java +++ b/jmeter-java-dsl-cli/src/test/java/us/abstracta/jmeter/javadsl/jmx2dsl/Jmx2DslIT.java @@ -18,8 +18,8 @@ public class Jmx2DslIT { @Test public void shouldGetConvertedFileWhenConvert() throws Exception { Process p = startCommand("jmx2dsl", new TestResource("test-plan.jmx").filePath()); - assertThat(getProcessOutput(p).replace("\r\n", "\n")) - .isEqualTo(buildConvertedTestClass().replace("\r\n", "\n")); + assertThat(getProcessOutput(p)) + .isEqualTo(buildConvertedTestClass()); } private Process startCommand(String command, String... args) throws IOException { diff --git a/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java b/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java index a2ab9d3a..73d9e3d3 100644 --- a/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java +++ b/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java @@ -23,30 +23,51 @@ import us.abstracta.jmeter.javadsl.codegeneration.params.StringParam; import us.abstracta.jmeter.javadsl.core.samplers.BaseSampler; -/** - * Allows to create a WebSocket sampler to establish a connection, send a - * message, read a response and disconnect. - * - * @since 2.2 - */ public class DslWebsocketSampler extends BaseSampler { private static final String DEFAULT_NAME = "Websocket Sampler"; - /** - * Creates a WebSocket sampler. - * - * @param name the name of the sampler - * @return the sampler for further configuration or usage - * @since 2.2 - */ private DslWebsocketSampler(String name) { super(name == null ? DEFAULT_NAME : name, OpenWebSocketSamplerGui.class); } /** - * Creates a WebSocket sampler. + * Provides factory methods to create WebSocket samplers for performance + * testing. + *

+ * This class serves as the entry point for creating different types of + * WebSocket operations: + *

    + *
  • {@link #connect(String)} - Establish a WebSocket connection
  • + *
  • {@link #write(String)} - Send messages to the server
  • + *
  • {@link #read()} - Read responses from the server
  • + *
  • {@link #disconnect()} - Close the WebSocket connection
  • + *
+ *

+ * Example usage: + * + *

{@code
+   *import static us.abstracta.jmeter.javadsl.JmeterDsl.*;
+   *import static us.abstracta.jmeter.javadsl.websocket.DslWebsocketSampler.webSocketSampler;
+   *import us.abstracta.jmeter.javadsl.core.TestPlanStats;
+   * 
+   *public class Test {
+   * 
+   *  public static void main(String[] args) throws Exception {
+   *    TestPlanStats stats = testPlan(
+   *      threadGroup(1, 1,
+   *        webSocketSampler().connect("wss://server.com/websocket")
+   *        webSocketSampler().write("Hello WebSocket!")
+   *        webSocketSampler().read()
+   *          .children(
+   *            responseAssertion()
+   *              .equalsToStrings("Hello WebSocket!")),
+   *        webSocketSampler().disconnect()
+   *      )
+   *    ).run();
+   *   }
+   * }
+   * }
* - * @return the sampler for further configuration or usage * @since 2.2 */ public static DslWebsocketSampler webSocketSampler() { @@ -54,30 +75,54 @@ public static DslWebsocketSampler webSocketSampler() { } /** - * Creates a WebSocket connect sampler. + * Creates a WebSocket connect sampler to establish a connection to the server. + *

+ * After establishing the connection, use {@link #write(String)} and + * {@link #read()} + * samplers to send and receive messages. Remember to close the connection using + * {@link #disconnect()} when finished. * * @return the connect sampler for further configuration or usage * @since 2.2 + * @see #link {@link DslConnectSampler#server(String)} + * @see #link {@link DslConnectSampler#port(int)} + * @see #link {@link DslConnectSampler#path(String)} + * @see #link {@link DslConnectSampler#tls(boolean)} */ public static DslConnectSampler connect() { return new DslConnectSampler(); } /** - * Creates a WebSocket connect sampler with URL parsing. + * Creates a WebSocket connect sampler to establish a connection to the server. + *

+ * After establishing the connection, use {@link #write(String)} and + * {@link #read()} + * samplers to send and receive messages. Remember to close the connection using + * {@link #disconnect()} when finished. + *

+ * It could be also used alone to test the connection to the server. + *

+ * URL Format: {@code ws://host:port/path?query} or + * {@code wss://host:port/path?query} + *

* - * @param url the WebSocket server URL + * @param url the WebSocket server URL. Supported schemes: {@code ws://} (plain) + * and {@code wss://} (TLS) * @return the connect sampler for further configuration or usage * @since 2.2 */ public static DslConnectSampler connect(String url) { - DslConnectSampler sampler = new DslConnectSampler(); - sampler.parseUrl(url); - return sampler; + return new DslConnectSampler(url); } /** - * Creates a WebSocket disconnect sampler. + * Creates WebSocket disconnect sampler to gracefully close the connection to + * the + *

+ * This sampler sends a close frame to the server and waits for the server's + * close frame response. It is recommended to always close connections + * explicitly to properly release resources. * * @return the disconnect sampler for further configuration or usage * @since 2.2 @@ -87,18 +132,43 @@ public static DslDisconnectSampler disconnect() { } /** - * Creates a WebSocket write sampler. - * + * Creates a WebSocket write sampler to send a text message to the server. + *

+ * Requires an active WebSocket connection established via + * {@link #connect(String)}. + * * @return the write sampler for further configuration or usage * @since 2.2 + * @see #link {@link DslWriteSampler#requestData(String)} */ public static DslWriteSampler write() { return new DslWriteSampler(); } /** - * Creates a WebSocket read sampler. - * + * Creates a WebSocket write sampler to send a text message to the server. + *

+ * Requires an active WebSocket connection established via + * {@link #connect(String)}. + * + * @return the write sampler for further configuration or usage + * @param requestData the message to send to the WebSocket server + * @since 2.2 + */ + public static DslWriteSampler write(String requestData) { + return new DslWriteSampler(requestData); + } + + /** + * Creates a WebSocket read sampler to receive a message from the server. + *

+ * By default, this sampler blocks execution until a response is received or + * the timeout is reached. This behavior can be changed using + * {@link DslReadSampler#waitForResponse(boolean)}. + *

+ * Requires an active WebSocket connection established via + * {@link #connect(String)}. + * * @return the read sampler for further configuration or usage * @since 2.2 */ @@ -112,28 +182,20 @@ protected TestElement buildTestElement() { "Use specific sampler types: connect(), disconnect(), write(), or read()"); } - /** - * Inner class for WebSocket connect operations. - */ public static class DslConnectSampler extends BaseSampler { private String connectionTimeout; private String responseTimeout; private String server; private String port; private String path; - private boolean tls; + private boolean tls = false; private DslConnectSampler() { super("WebSocket Open Connection", OpenWebSocketSamplerGui.class); } - /** - * Parses a WebSocket URL and sets the corresponding fields. - * - * @param url the WebSocket URL to parse - * @throws IllegalArgumentException if the URL is invalid - */ - private void parseUrl(String url) { + private DslConnectSampler(String url) { + super("WebSocket Open Connection", OpenWebSocketSamplerGui.class); try { URI uri = new URI(url); @@ -165,7 +227,7 @@ private void parseUrl(String url) { String query = uri.getQuery(); if (query != null && !query.isEmpty()) { - this.path(this.getPath() + "?" + query); + this.path(this.path + "?" + query); } } catch (URISyntaxException e) { @@ -176,8 +238,12 @@ private void parseUrl(String url) { @Override protected TestElement buildTestElement() { OpenWebSocketSampler ret = new OpenWebSocketSampler(); - ret.setConnectTimeout(connectionTimeout); - ret.setReadTimeout(responseTimeout); + if (connectionTimeout != null) { + ret.setConnectTimeout(connectionTimeout); + } + if (responseTimeout != null) { + ret.setReadTimeout(responseTimeout); + } ret.setTLS(tls); ret.setServer(server); ret.setPort(port); @@ -186,9 +252,12 @@ protected TestElement buildTestElement() { } /** - * Sets the connection timeout for the WebSocket connection. - * - * @param timeout the connection timeout in milliseconds + * Same as {@link #connectionTimeout(int)} but allowing to use JMeter + * expressions + * (variables or + * functions) to solve the actual parameter values. + * + * @param timeout a JMeter expression that returns timeout in milliseconds * @return the sampler for further configuration or usage * @since 2.2 */ @@ -198,9 +267,24 @@ public DslConnectSampler connectionTimeout(String timeout) { } /** - * Sets the response timeout for the WebSocket operations. + * Sets the connection timeout for the WebSocket connection creation. * - * @param timeout the response timeout in milliseconds + * @param timeout the connection timeout in milliseconds + * @return the sampler for further configuration or usage + * @since 2.2 + */ + public DslConnectSampler connectionTimeout(int timeout) { + this.connectionTimeout = String.valueOf(timeout); + return this; + } + + /** + * Same as {@link #responseTimeout(int)} but allowing to use JMeter + * expressions + * (variables or + * functions) to solve the actual parameter values. + * + * @param timeout a JMeter expression that returns timeout in milliseconds * @return the sampler for further configuration or usage * @since 2.2 */ @@ -209,6 +293,18 @@ public DslConnectSampler responseTimeout(String timeout) { return this; } + /** + * Sets the response timeout for the WebSocket negotiation. + * + * @param timeout the response timeout in milliseconds + * @return the sampler for further configuration or usage + * @since 2.2 + */ + public DslConnectSampler responseTimeout(int timeout) { + this.responseTimeout = String.valueOf(timeout); + return this; + } + /** * Specifies the WebSocket server to connect to. * @@ -222,7 +318,7 @@ public DslConnectSampler server(String server) { } /** - * Sets the WebSocket port to connect to. + * Specifies the WebSocket port to connect to. * * @param port the WebSocket port to connect to * @return the sampler for further configuration or usage @@ -234,7 +330,26 @@ public DslConnectSampler port(String port) { } /** - * Sets the WebSocket path to connect to. + * Specifies the WebSocket port to connect to. + * + * @param port the WebSocket port to connect to + * @return the sampler for further configuration or usage + * @since 2.2 + */ + public DslConnectSampler port(int port) { + this.port = String.valueOf(port); + return this; + } + + /** + * Specifies the WebSocket path to connect to. In case of need query parameters, + * they should be included in the path. + *

+ * Example: + * + *

{@code
+     * path("/websocket?room=general")
+     * }
* * @param path the WebSocket path to connect to * @return the sampler for further configuration or usage @@ -245,12 +360,8 @@ public DslConnectSampler path(String path) { return this; } - private String getPath() { - return path; - } - /** - * Sets the WebSocket TLS flag. + * Specifies if the WebSocket connection should be established using TLS. * * @param tls the WebSocket TLS flag * @return the sampler for further configuration or usage @@ -275,45 +386,29 @@ protected MethodCall buildMethodCall(OpenWebSocketSampler testElement, MethodParam port = paramBuilder.stringParam("port"); MethodParam path = paramBuilder.stringParam("path"); MethodParam tls = paramBuilder.boolParam("TLS", false); - - // Try to build URL if all required parameters are available + MethodCall ret = null; if (!server.isDefault() && !port.isDefault() && !path.isDefault()) { String protocol = tls.isDefault() ? "ws" : (tls.getExpression().equals("true") ? "wss" : "ws"); String url = protocol + "://" + server.getExpression() + ":" + port.getExpression() + path.getExpression(); - MethodCall ret = new MethodCall("webSocketSampler().connect", + ret = new MethodCall("webSocketSampler().connect", DslConnectSampler.class, new StringParam(url)); - - // Add non-default timeout parameters - MethodParam connectionTimeout = paramBuilder.stringParam("connectTimeout"); - if (!connectionTimeout.isDefault()) { - ret.chain("connectionTimeout", connectionTimeout); - } - MethodParam responseTimeout = paramBuilder.stringParam("readTimeout"); - if (!responseTimeout.isDefault()) { - ret.chain("responseTimeout", responseTimeout); - } - - return ret; } else { - // Fall back to individual method calls - return buildMethodCall() + ret = buildMethodCall() .chain("webSocketSampler().connect()") .chain("server", server) .chain("port", port) .chain("path", path) - .chain("tls", tls) - .chain("connectionTimeout", paramBuilder.stringParam("connectTimeout")) - .chain("responseTimeout", paramBuilder.stringParam("readTimeout")); + .chain("tls", tls); } + return ret + .chain("connectionTimeout", paramBuilder.stringParam("connectTimeout", "")) + .chain("responseTimeout", paramBuilder.stringParam("readTimeout", "")); } } } - /** - * Inner class for WebSocket disconnect operations. - */ public static class DslDisconnectSampler extends BaseSampler { private String responseTimeout; private String statusCode; @@ -325,15 +420,22 @@ private DslDisconnectSampler() { @Override protected TestElement buildTestElement() { CloseWebSocketSampler close = new CloseWebSocketSampler(); - close.setReadTimeout(responseTimeout); - close.setStatusCode(statusCode); + if (responseTimeout != null) { + close.setReadTimeout(responseTimeout); + } + if (statusCode != null) { + close.setStatusCode(statusCode); + } return close; } /** - * Sets the response timeout for the WebSocket operations. - * - * @param timeout the response timeout in milliseconds + * Same as {@link #responseTimeout(int)} but allowing to use JMeter + * expressions + * (variables or + * functions) to solve the actual parameter values. + * + * @param timeout a JMeter expression that returns timeout in milliseconds * @return the sampler for further configuration or usage * @since 2.2 */ @@ -343,9 +445,25 @@ public DslDisconnectSampler responseTimeout(String timeout) { } /** - * Sets the status code for the WebSocket disconnect. + * Sets the response timeout for the close frame from server to be received. * - * @param statusCode the status code for the WebSocket disconnect + * @param timeout the response timeout in milliseconds + * @return the sampler for further configuration or usage + * @since 2.2 + */ + public DslDisconnectSampler responseTimeout(int timeout) { + this.responseTimeout = String.valueOf(timeout); + return this; + } + + /** + * Same as {@link #statusCode(int)} but allowing to use JMeter + * expressions + * (variables or + * functions) to solve the actual parameter values. + * + * @param statusCode a JMeter expression that returns the status code for the + * WebSocket disconnect * @return the sampler for further configuration or usage * @since 2.2 */ @@ -354,6 +472,30 @@ public DslDisconnectSampler statusCode(String statusCode) { return this; } + /** + * Sets the status code to indicate the reason for closing the connection. + *

+ * Common status codes: + *

    + *
  • 1000 - Normal closure (default)
  • + *
  • 1001 - Going away
  • + *
  • 1002 - Protocol error
  • + *
  • 1003 - Unsupported data
  • + *
+ *

+ * For a complete list, see + * RFC + * 6455 Section 7.4 + * + * @param statusCode the status code for the WebSocket disconnect + * @return the sampler for further configuration or usage + * @since 2.2 + */ + public DslDisconnectSampler statusCode(int statusCode) { + this.statusCode = String.valueOf(statusCode); + return this; + } + public static class CodeBuilder extends SingleTestElementCallBuilder { public CodeBuilder(List builderMethods) { @@ -371,40 +513,27 @@ protected MethodCall buildMethodCall(CloseWebSocketSampler testElement, } } - /** - * Inner class for WebSocket write operations. - */ public static class DslWriteSampler extends BaseSampler { - private String connectionTimeout; private String requestData; - private boolean createNewConnection = false; private DslWriteSampler() { super("WebSocket Single Write", SingleWriteWebSocketSamplerGui.class); } + private DslWriteSampler(String requestData) { + super("WebSocket Single Write", SingleWriteWebSocketSamplerGui.class); + this.requestData = requestData; + } + @Override protected TestElement buildTestElement() { SingleWriteWebSocketSampler write = new SingleWriteWebSocketSampler(); - write.setConnectTimeout(connectionTimeout != null ? connectionTimeout : "20000"); write.setType(DataPayloadType.Text); write.setRequestData(requestData); - write.setCreateNewConnection(createNewConnection); + write.setCreateNewConnection(false); return write; } - /** - * Sets the connection timeout for the WebSocket connection. - * - * @param timeout the connection timeout in milliseconds - * @return the sampler for further configuration or usage - * @since 2.2 - */ - public DslWriteSampler connectionTimeout(String timeout) { - this.connectionTimeout = timeout; - return this; - } - /** * Sets the request data for the WebSocket write. * @@ -417,18 +546,6 @@ public DslWriteSampler requestData(String requestData) { return this; } - /** - * Sets the create new connection flag for the WebSocket write. - * - * @param createNewConnection the create new connection flag for the WebSocket write - * @return the sampler for further configuration or usage - * @since 2.2 - */ - public DslWriteSampler createNewConnection(boolean createNewConnection) { - this.createNewConnection = createNewConnection; - return this; - } - public static class CodeBuilder extends SingleTestElementCallBuilder { @@ -447,13 +564,9 @@ protected MethodCall buildMethodCall(SingleWriteWebSocketSampler testElement, } } - /** - * Inner class for WebSocket read operations. - */ public static class DslReadSampler extends BaseSampler { - private String connectionTimeout; private String responseTimeout; - private boolean createNewConnection = false; + private boolean waitForResponse = true; private DslReadSampler() { super("WebSocket Single Read", SingleReadWebSocketSamplerGui.class); @@ -462,39 +575,57 @@ private DslReadSampler() { @Override protected TestElement buildTestElement() { SingleReadWebSocketSampler read = new SingleReadWebSocketSampler(); - read.setConnectTimeout(connectionTimeout != null ? connectionTimeout : "20000"); - read.setReadTimeout(responseTimeout != null ? responseTimeout : "7000"); + if (responseTimeout != null) { + read.setReadTimeout(responseTimeout); + } read.setDataType(DataType.Text); - read.setOptional(false); - read.setCreateNewConnection(createNewConnection); + read.setOptional(!waitForResponse); + read.setCreateNewConnection(false); return read; } /** - * Sets the connection timeout for the WebSocket connection. + * Specifies whether the sampler should block execution until a response is + * received. + *

+ * When set to {@code true} (default), the sampler waits for a server response + * or until the timeout expires. When set to {@code false}, the sampler returns + * immediately if no message is available. * - * @param timeout the connection timeout in milliseconds + * @param waitForResponse {@code true} to block until response is received, + * {@code false} to return immediately * @return the sampler for further configuration or usage * @since 2.2 */ - public DslReadSampler connectionTimeout(String timeout) { - this.connectionTimeout = timeout; + public DslReadSampler waitForResponse(boolean waitForResponse) { + this.waitForResponse = waitForResponse; return this; } /** - * Sets the response timeout for the WebSocket operations. - * - * @param timeout the response timeout in milliseconds + * Same as {@link #responseTimeout(int)} but allowing to use JMeter + * expressions + * (variables or + * functions) to solve the actual parameter values. + * + * @param timeout a JMeter expression that returns timeout in milliseconds * @return the sampler for further configuration or usage + * @since 2.2 */ public DslReadSampler responseTimeout(String timeout) { this.responseTimeout = timeout; return this; } - public DslReadSampler createNewConnection(boolean createNewConnection) { - this.createNewConnection = createNewConnection; + /** + * Sets the response timeout for the WebSocket response to be received. + * + * @param timeout the response timeout in milliseconds + * @return the sampler for further configuration or usage + * @since 2.2 + */ + public DslReadSampler responseTimeout(int timeout) { + this.responseTimeout = String.valueOf(timeout); return this; } @@ -510,7 +641,7 @@ protected MethodCall buildMethodCall(SingleReadWebSocketSampler testElement, MethodCallContext context) { TestElementParamBuilder paramBuilder = new TestElementParamBuilder(testElement); return new MethodCall("webSocketSampler().read", DslReadSampler.class) - .chain("responseTimeout", paramBuilder.stringParam("readTimeout")) + .chain("responseTimeout", paramBuilder.stringParam("readTimeout", "")) .chain("createNewConnection", paramBuilder.boolParam("createNewConnection", false)); } } diff --git a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketCodeGeneratorTest.java b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketCodeGeneratorTest.java index 72bcc52e..bbd84ef6 100644 --- a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketCodeGeneratorTest.java +++ b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketCodeGeneratorTest.java @@ -55,16 +55,6 @@ public void shouldGenerateExpectedCodeWhenWebSocketWithVariablesJmxIsProvided(@T "WebSocketWithVariablesTest.java")); } - @Test - public void shouldGenerateCommentedElementsCodeWhenDisabledWebSocketElementsInJmx(@TempDir Path tempDir) throws Exception { - assertThat(new DslCodeGenerator() - .addBuildersFrom(DslWebsocketSampler.class) - .addDependency(DslWebsocketSampler.class, "us.abstracta.jmeter:jmeter-java-dsl-websocket") - .generateCodeFromJmx(new TestResource(RESOURCES_FOLDER + "/disabled-websocket-elements.jmx").file())) - .isEqualToNormalizingNewlines(solveTestClassTemplate(Collections.emptySet(), - "DisabledWebSocketElements.java")); - } - private File solveTemplateResource(String resourcePath, Path tempDir) throws IOException { String templateContents = new StringTemplate(new TestResource(RESOURCES_FOLDER + "/" + resourcePath).rawContents()) .solve(); diff --git a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java index 8eaf18f0..0a816f93 100644 --- a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java +++ b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java @@ -21,12 +21,9 @@ public void shouldConnectAndEchoMessageWhenWebSocketTestPlanWithEchoServer() thr .responseTimeout("5000"), webSocketSampler() .write() - .requestData("Hello WebSocket Test!") - .createNewConnection(false), + .requestData("Hello WebSocket Test!"), webSocketSampler() .read() - .connectionTimeout("10000") - .createNewConnection(false) .responseTimeout("5000") .children( responseAssertion() @@ -47,7 +44,7 @@ public void shouldThrowIllegalArgumentExceptionWhenConnectWithInvalidUrl() { } @Test - public void shouldHandleConnectionFailureWhenConnectToUnavailableServer() throws Exception { + public void shouldErrorSamplerWhenConnectToUnavailableServer() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, webSocketSampler() @@ -59,7 +56,7 @@ public void shouldHandleConnectionFailureWhenConnectToUnavailableServer() throws } @Test - public void shouldHandleTimeoutWhenConnectWithVeryShortTimeout() throws Exception { + public void shouldErrorSamplerWhenConnectWithVeryShortTimeout() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, webSocketSampler() @@ -71,31 +68,29 @@ public void shouldHandleTimeoutWhenConnectWithVeryShortTimeout() throws Exceptio } @Test - public void shouldHandleWriteOperationWhenNoPreviousConnection() throws Exception { + public void shouldErrorSamplerWhenWriteOperationWhenNoPreviousConnection() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, webSocketSampler() .write() - .requestData("Test message") - .createNewConnection(false))) + .requestData("Test message"))) .run(); assertThat(stats.overall().errorsCount()).isEqualTo(1); } @Test - public void shouldHandleReadOperationWhenNoPreviousConnection() throws Exception { + public void shouldErrorSamplerWhenReadOperationWhenNoPreviousConnection() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, webSocketSampler() .read() - .createNewConnection(false) .responseTimeout("1000"))) .run(); assertThat(stats.overall().errorsCount()).isEqualTo(1); } @Test - public void shouldHandleDisconnectOperationWhenNoPreviousConnection() throws Exception { + public void shouldErrorSamplerWhenDisconnectOperationWhenNoPreviousConnection() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, webSocketSampler() diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElements.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElements.java deleted file mode 100644 index 47652a11..00000000 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElements.java +++ /dev/null @@ -1,16 +0,0 @@ -testPlan( - threadGroup(1, 1, - webSocketSampler().connect("ws://ws.postman-echo.com:80/raw") - .connectionTimeout("10000") - .responseTimeout("5000"), - //webSocketSampler().write() - // .requestData("Disabled write message"), - webSocketSampler().read() - .responseTimeout("5000"), - //webSocketSampler().write() - // .requestData("Another disabled write"), - webSocketSampler().disconnect() - .responseTimeout("1000") - .statusCode("1000") - ) - ) \ No newline at end of file diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElementsTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElementsTest.java deleted file mode 100644 index e571d79e..00000000 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/DisabledWebSocketElementsTest.java +++ /dev/null @@ -1,28 +0,0 @@ - testPlan( - threadGroup(1, 1, - webSocketSampler() - .connect() - .server("ws.postman-echo.com") - .port("80") - .path("/raw") - .tls(false) - .connectionTimeout("10000") - .responseTimeout("5000"), - // webSocketSampler() - // .write() - // .requestData("Disabled write message") - // .createNewConnection(false), - webSocketSampler() - .read() - .responseTimeout("5000") - .createNewConnection(false), - // webSocketSampler() - // .write() - // .requestData("Another disabled write") - // .createNewConnection(false), - webSocketSampler() - .disconnect() - .responseTimeout("1000") - .statusCode("1000") - ) - ) diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/disabled-websocket-elements.jmx b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/disabled-websocket-elements.jmx deleted file mode 100644 index a3b06e7f..00000000 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/disabled-websocket-elements.jmx +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - false - true - false - - - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - 10000 - 5000 - false - ws.postman-echo.com - 80 - /raw - - - - 20000 - Text - Disabled write message - false - false - - 80 - - false - - - - - 20000 - 5000 - Text - false - false - false - - 80 - - - - - 20000 - Text - Another disabled write - false - false - - 80 - - false - - - - - 1000 - 1000 - - - - - - diff --git a/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/codegeneration/MethodCallBuilderTest.java b/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/codegeneration/MethodCallBuilderTest.java index 5032b2bb..0c01d14a 100644 --- a/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/codegeneration/MethodCallBuilderTest.java +++ b/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/codegeneration/MethodCallBuilderTest.java @@ -68,7 +68,7 @@ private Map extractCodeBuilderTestCodes() { .getMethods().stream() .collect( Collectors.toMap(NodeWithSimpleName::getNameAsString, - m -> m.getBody().map(Objects::toString).orElse("").replace("\r\n", "\n"))); + m -> m.getBody().map(Objects::toString).orElse(""))); } @ParameterizedTest(name = "{0}") diff --git a/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/StringTemplateAssert.java b/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/StringTemplateAssert.java index 649be002..3ed8e133 100644 --- a/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/StringTemplateAssert.java +++ b/jmeter-java-dsl/src/test/java/us/abstracta/jmeter/javadsl/core/StringTemplateAssert.java @@ -43,7 +43,7 @@ public static StringTemplateAssertString assertThat(String actual) { protected abstract ErrorMessageFactory getErrorMessageFactory(List> diffs); public SELF matches(TestResource template) throws IOException { - return matches(template.rawContents().replace("\r\n", "\n")); + return matches(template.rawContents()); } public SELF matches(String templateContents) throws IOException { From e267e4a971fbb5082ff30fe0bb6fa9517fcec2b4 Mon Sep 17 00:00:00 2001 From: Sebastian Lorenzo Date: Mon, 8 Dec 2025 17:20:12 -0300 Subject: [PATCH 09/11] Fix codestyle --- .../jmeter/javadsl/websocket/DslWebsocketSampler.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java b/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java index 73d9e3d3..f261ad15 100644 --- a/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java +++ b/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java @@ -83,11 +83,11 @@ public static DslWebsocketSampler webSocketSampler() { * {@link #disconnect()} when finished. * * @return the connect sampler for further configuration or usage - * @since 2.2 * @see #link {@link DslConnectSampler#server(String)} * @see #link {@link DslConnectSampler#port(int)} * @see #link {@link DslConnectSampler#path(String)} * @see #link {@link DslConnectSampler#tls(boolean)} + * @since 2.2 */ public static DslConnectSampler connect() { return new DslConnectSampler(); @@ -138,8 +138,8 @@ public static DslDisconnectSampler disconnect() { * {@link #connect(String)}. * * @return the write sampler for further configuration or usage - * @since 2.2 * @see #link {@link DslWriteSampler#requestData(String)} + * @since 2.2 */ public static DslWriteSampler write() { return new DslWriteSampler(); @@ -151,8 +151,8 @@ public static DslWriteSampler write() { * Requires an active WebSocket connection established via * {@link #connect(String)}. * - * @return the write sampler for further configuration or usage * @param requestData the message to send to the WebSocket server + * @return the write sampler for further configuration or usage * @since 2.2 */ public static DslWriteSampler write(String requestData) { From 356b81c1d41be2ba97362e3d94f835c2e24962e5 Mon Sep 17 00:00:00 2001 From: Sebastian Lorenzo Date: Tue, 9 Dec 2025 15:09:17 -0300 Subject: [PATCH 10/11] Refactors Remove connection and write methods to use only constructor way Add enum for status code Change code builders to use common parameters types Update test Update doc --- docs/guide/protocols/websocket.md | 18 +- .../jmeter/javadsl/cli/Jmx2DslCommand.java | 4 +- jmeter-java-dsl-websocket/pom.xml | 6 + ...tSampler.java => DslWebsocketFactory.java} | 463 +++++++----------- .../test/java/DslWebsocketSamplerTest.java | 56 +-- .../src/test/java/WebSocketEchoServer.java | 48 ++ ...t.java => WebsocketCodeGeneratorTest.java} | 29 +- .../ComplexWebSocketTest.java | 20 +- .../SimpleWebSocketTest.java | 14 +- .../WebSocketWithAssertionsTest.java | 18 - .../WebSocketWithVariablesTest.java | 23 +- .../complex-websocket.jmx | 12 +- .../websocket-test-plan.template.jmx | 35 +- .../websocket-with-assertions.jmx | 82 ---- .../websocket-with-variables.jmx | 34 +- 15 files changed, 328 insertions(+), 534 deletions(-) rename jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/{DslWebsocketSampler.java => DslWebsocketFactory.java} (53%) create mode 100644 jmeter-java-dsl-websocket/src/test/java/WebSocketEchoServer.java rename jmeter-java-dsl-websocket/src/test/java/{DslWebsocketCodeGeneratorTest.java => WebsocketCodeGeneratorTest.java} (72%) delete mode 100644 jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithAssertionsTest.java delete mode 100644 jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-with-assertions.jmx diff --git a/docs/guide/protocols/websocket.md b/docs/guide/protocols/websocket.md index fa60863f..1428615f 100644 --- a/docs/guide/protocols/websocket.md +++ b/docs/guide/protocols/websocket.md @@ -28,21 +28,21 @@ Following you can see a basic usage example of Web Socket protocol. ```java import static us.abstracta.jmeter.javadsl.JmeterDsl.*; -import static us.abstracta.jmeter.javadsl.websocket.DslWebsocketSampler.webSocketSampler; +import us.abstracta.jmeter.javadsl.websocket.DslWebsocketFactory; import us.abstracta.jmeter.javadsl.core.TestPlanStats; public class Test { public static void main(String[] args) throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - webSocketSampler().connect("wss://ws.postman-echo.com/raw"), - webSocketSampler().write("Hello WebSocket!"), - webSocketSampler().read() + DslWebsocketFactory.websocketConnect("wss://ws.postman-echo.com/raw"), + DslWebsocketFactory.websocketWrite("Hello WebSocket!"), + DslWebsocketFactory.websocketRead() .children( responseAssertion() .equalsToStrings("Hello WebSocket!") ), - webSocketSampler().disconnect() + DslWebsocketFactory.websocketDisconnect() ) ).run(); } @@ -53,16 +53,16 @@ public class Test { Only `ws://` and `wss://` protocols are supported. Using any other scheme will throw an `IllegalArgumentException`. ::: +::: tip You can use a non blocking read if it is necessary in the following way ```java -webSocketSampler().read().waitForResponse(false) +DslWebsocketFactory.websocketRead().waitForResponse(false) ``` -::: warning In this case is not recommended to add an assertion due the response could be empty ::: -::: tip -Web Socket protocol only supports one connection at a time. If you want to change Web Socket server during execution you should add a disconnect sampler and then establish a new connection. +::: warning +Web Socket plugin only supports one connection for threads at a time. If you want to change Web Socket server during execution you should add a disconnect sampler and then establish a new connection. ::: \ No newline at end of file diff --git a/jmeter-java-dsl-cli/src/main/java/us/abstracta/jmeter/javadsl/cli/Jmx2DslCommand.java b/jmeter-java-dsl-cli/src/main/java/us/abstracta/jmeter/javadsl/cli/Jmx2DslCommand.java index 5b6e7edb..6df9fad3 100644 --- a/jmeter-java-dsl-cli/src/main/java/us/abstracta/jmeter/javadsl/cli/Jmx2DslCommand.java +++ b/jmeter-java-dsl-cli/src/main/java/us/abstracta/jmeter/javadsl/cli/Jmx2DslCommand.java @@ -11,7 +11,7 @@ import us.abstracta.jmeter.javadsl.graphql.DslGraphqlSampler; import us.abstracta.jmeter.javadsl.jdbc.JdbcJmeterDsl; import us.abstracta.jmeter.javadsl.parallel.ParallelController; -import us.abstracta.jmeter.javadsl.websocket.DslWebsocketSampler; +import us.abstracta.jmeter.javadsl.websocket.DslWebsocketFactory; import us.abstracta.jmeter.javadsl.wrapper.WrapperJmeterDsl; @Command(name = "jmx2dsl", header = "Converts a JMX file to DSL code", @@ -49,7 +49,7 @@ public Integer call() throws Exception { addBuildersFrom(ElasticsearchBackendListener.class, "jmeter-java-dsl-elasticsearch-listener", codeGenerator); addBuildersFrom(DatadogBackendListener.class, "jmeter-java-dsl-datadog", codeGenerator); - addBuildersFrom(DslWebsocketSampler.class, "jmeter-java-dsl-websocket", codeGenerator); + addBuildersFrom(DslWebsocketFactory.class, "jmeter-java-dsl-websocket", codeGenerator); System.out.println(codeGenerator.generateCodeFromJmx(jmxFile)); return 0; } diff --git a/jmeter-java-dsl-websocket/pom.xml b/jmeter-java-dsl-websocket/pom.xml index a889883e..7855080d 100644 --- a/jmeter-java-dsl-websocket/pom.xml +++ b/jmeter-java-dsl-websocket/pom.xml @@ -33,6 +33,12 @@ + + org.java-websocket + Java-WebSocket + 1.6.0 + test + \ No newline at end of file diff --git a/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java b/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketFactory.java similarity index 53% rename from jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java rename to jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketFactory.java index f261ad15..b71a73dd 100644 --- a/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketSampler.java +++ b/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketFactory.java @@ -20,86 +20,63 @@ import us.abstracta.jmeter.javadsl.codegeneration.MethodParam; import us.abstracta.jmeter.javadsl.codegeneration.SingleTestElementCallBuilder; import us.abstracta.jmeter.javadsl.codegeneration.TestElementParamBuilder; +import us.abstracta.jmeter.javadsl.codegeneration.params.BoolParam; +import us.abstracta.jmeter.javadsl.codegeneration.params.EnumParam; import us.abstracta.jmeter.javadsl.codegeneration.params.StringParam; import us.abstracta.jmeter.javadsl.core.samplers.BaseSampler; -public class DslWebsocketSampler extends BaseSampler { - private static final String DEFAULT_NAME = "Websocket Sampler"; - - private DslWebsocketSampler(String name) { - super(name == null ? DEFAULT_NAME : name, OpenWebSocketSamplerGui.class); - } - - /** - * Provides factory methods to create WebSocket samplers for performance - * testing. - *

- * This class serves as the entry point for creating different types of - * WebSocket operations: - *

    - *
  • {@link #connect(String)} - Establish a WebSocket connection
  • - *
  • {@link #write(String)} - Send messages to the server
  • - *
  • {@link #read()} - Read responses from the server
  • - *
  • {@link #disconnect()} - Close the WebSocket connection
  • - *
- *

- * Example usage: - * - *

{@code
-   *import static us.abstracta.jmeter.javadsl.JmeterDsl.*;
-   *import static us.abstracta.jmeter.javadsl.websocket.DslWebsocketSampler.webSocketSampler;
-   *import us.abstracta.jmeter.javadsl.core.TestPlanStats;
-   * 
-   *public class Test {
-   * 
-   *  public static void main(String[] args) throws Exception {
-   *    TestPlanStats stats = testPlan(
-   *      threadGroup(1, 1,
-   *        webSocketSampler().connect("wss://server.com/websocket")
-   *        webSocketSampler().write("Hello WebSocket!")
-   *        webSocketSampler().read()
-   *          .children(
-   *            responseAssertion()
-   *              .equalsToStrings("Hello WebSocket!")),
-   *        webSocketSampler().disconnect()
-   *      )
-   *    ).run();
-   *   }
-   * }
-   * }
- * - * @since 2.2 - */ - public static DslWebsocketSampler webSocketSampler() { - return new DslWebsocketSampler(DEFAULT_NAME); - } - - /** - * Creates a WebSocket connect sampler to establish a connection to the server. - *

- * After establishing the connection, use {@link #write(String)} and - * {@link #read()} - * samplers to send and receive messages. Remember to close the connection using - * {@link #disconnect()} when finished. - * - * @return the connect sampler for further configuration or usage - * @see #link {@link DslConnectSampler#server(String)} - * @see #link {@link DslConnectSampler#port(int)} - * @see #link {@link DslConnectSampler#path(String)} - * @see #link {@link DslConnectSampler#tls(boolean)} - * @since 2.2 - */ - public static DslConnectSampler connect() { - return new DslConnectSampler(); +/** + * Provides factory methods to create WebSocket samplers for performance + * testing. + *

+ * This class serves as the entry point for creating different types of + * WebSocket operations: + *

    + *
  • {@link #websocketConnect(String)} - Establish a WebSocket connection
  • + *
  • {@link #websocketWrite(String)} - Send messages to the server
  • + *
  • {@link #websocketRead()} - Read responses from the server
  • + *
  • {@link #websocketDisconnect()} - Close the WebSocket connection
  • + *
+ *

+ * Example usage: + * + *

{@code
+ *import static us.abstracta.jmeter.javadsl.JmeterDsl.*;
+ *import static us.abstracta.jmeter.javadsl.websocket.DslWebsocketSampler.webSocketSampler;
+ *import us.abstracta.jmeter.javadsl.core.TestPlanStats;
+ * 
+ *public class Test {
+ * 
+ *  public static void main(String[] args) throws Exception {
+ *    TestPlanStats stats = testPlan(
+ *      threadGroup(1, 1,
+ *        webSocketSampler().connect("wss://server.com/websocket")
+ *        webSocketSampler().write("Hello WebSocket!")
+ *        webSocketSampler().read()
+ *          .children(
+ *            responseAssertion()
+ *              .equalsToStrings("Hello WebSocket!")),
+ *        webSocketSampler().disconnect()
+ *      )
+ *    ).run();
+ *   }
+ * }
+ * }
+ * + * @since 2.2 + */ +public class DslWebsocketFactory { + + private DslWebsocketFactory() { } /** * Creates a WebSocket connect sampler to establish a connection to the server. *

- * After establishing the connection, use {@link #write(String)} and - * {@link #read()} + * After establishing the connection, use {@link #websocketWrite(String)} and + * {@link #websocketRead()} * samplers to send and receive messages. Remember to close the connection using - * {@link #disconnect()} when finished. + * {@link #websocketDisconnect()} when finished. *

* It could be also used alone to test the connection to the server. *

@@ -112,7 +89,7 @@ public static DslConnectSampler connect() { * @return the connect sampler for further configuration or usage * @since 2.2 */ - public static DslConnectSampler connect(String url) { + public static DslConnectSampler websocketConnect(String url) { return new DslConnectSampler(url); } @@ -127,7 +104,7 @@ public static DslConnectSampler connect(String url) { * @return the disconnect sampler for further configuration or usage * @since 2.2 */ - public static DslDisconnectSampler disconnect() { + public static DslDisconnectSampler websocketDisconnect() { return new DslDisconnectSampler(); } @@ -135,27 +112,13 @@ public static DslDisconnectSampler disconnect() { * Creates a WebSocket write sampler to send a text message to the server. *

* Requires an active WebSocket connection established via - * {@link #connect(String)}. - * - * @return the write sampler for further configuration or usage - * @see #link {@link DslWriteSampler#requestData(String)} - * @since 2.2 - */ - public static DslWriteSampler write() { - return new DslWriteSampler(); - } - - /** - * Creates a WebSocket write sampler to send a text message to the server. - *

- * Requires an active WebSocket connection established via - * {@link #connect(String)}. + * {@link #websocketConnect(String)}. * * @param requestData the message to send to the WebSocket server * @return the write sampler for further configuration or usage * @since 2.2 */ - public static DslWriteSampler write(String requestData) { + public static DslWriteSampler websocketWrite(String requestData) { return new DslWriteSampler(requestData); } @@ -167,33 +130,46 @@ public static DslWriteSampler write(String requestData) { * {@link DslReadSampler#waitForResponse(boolean)}. *

* Requires an active WebSocket connection established via - * {@link #connect(String)}. + * {@link #websocketConnect(String)}. * * @return the read sampler for further configuration or usage * @since 2.2 */ - public static DslReadSampler read() { + public static DslReadSampler websocketRead() { return new DslReadSampler(); } - @Override - protected TestElement buildTestElement() { - throw new UnsupportedOperationException( - "Use specific sampler types: connect(), disconnect(), write(), or read()"); + public enum StatusCode implements EnumParam.EnumPropertyValue { + NORMAL_CLOSURE("1000"), + GOING_AWAY("1001"), + PROTOCOL_ERROR("1002"), + UNSUPPORTED_DATA("1003"), + NO_STATUS_CODE_PRESENT("1005"), + MESSAGE_TYPE_ERROR("1007"), + POLICY_VIOLATION("1008"), + MESSAGE_TOO_BIG_ERROR("1009"), + TLS_HANDSHAKE_ERROR("1015"); + + private final String propertyValue; + + StatusCode(String propertyValue) { + this.propertyValue = propertyValue; + } + + @Override + public String propertyValue() { + return propertyValue; + } } public static class DslConnectSampler extends BaseSampler { - private String connectionTimeout; - private String responseTimeout; + private String connectionTimeoutMillis; + private String responseTimeoutMillis; private String server; private String port; private String path; private boolean tls = false; - private DslConnectSampler() { - super("WebSocket Open Connection", OpenWebSocketSamplerGui.class); - } - private DslConnectSampler(String url) { super("WebSocket Open Connection", OpenWebSocketSamplerGui.class); try { @@ -205,29 +181,29 @@ private DslConnectSampler(String url) { "Invalid WebSocket URL. Must start with 'ws://' or 'wss://'"); } - this.tls("wss".equals(scheme)); - this.server(uri.getHost()); + this.tls = "wss".equals(scheme); + this.server = uri.getHost(); if (this.server == null) { throw new IllegalArgumentException("Invalid WebSocket URL. Host is required"); } int port = uri.getPort(); if (port == -1) { - this.port(this.tls ? "443" : "80"); + this.port = this.tls ? "443" : "80"; } else { - this.port(String.valueOf(port)); + this.port = String.valueOf(port); } String path = uri.getPath(); if (path == null || path.isEmpty()) { - this.path("/"); + this.path = "/"; } else { - this.path(path); + this.path = path; } String query = uri.getQuery(); if (query != null && !query.isEmpty()) { - this.path(this.path + "?" + query); + this.path = this.path + "?" + query; } } catch (URISyntaxException e) { @@ -238,11 +214,11 @@ private DslConnectSampler(String url) { @Override protected TestElement buildTestElement() { OpenWebSocketSampler ret = new OpenWebSocketSampler(); - if (connectionTimeout != null) { - ret.setConnectTimeout(connectionTimeout); + if (connectionTimeoutMillis != null) { + ret.setConnectTimeout(connectionTimeoutMillis); } - if (responseTimeout != null) { - ret.setReadTimeout(responseTimeout); + if (responseTimeoutMillis != null) { + ret.setReadTimeout(responseTimeoutMillis); } ret.setTLS(tls); ret.setServer(server); @@ -251,124 +227,59 @@ protected TestElement buildTestElement() { return ret; } - /** - * Same as {@link #connectionTimeout(int)} but allowing to use JMeter - * expressions - * (variables or - * functions) to solve the actual parameter values. - * - * @param timeout a JMeter expression that returns timeout in milliseconds - * @return the sampler for further configuration or usage - * @since 2.2 - */ - public DslConnectSampler connectionTimeout(String timeout) { - this.connectionTimeout = timeout; - return this; - } - /** * Sets the connection timeout for the WebSocket connection creation. * - * @param timeout the connection timeout in milliseconds + * @param timeoutMillis the connection timeout in milliseconds (default value is + * 20000 milliseconds) * @return the sampler for further configuration or usage * @since 2.2 */ - public DslConnectSampler connectionTimeout(int timeout) { - this.connectionTimeout = String.valueOf(timeout); + public DslConnectSampler connectionTimeout(int timeoutMillis) { + this.connectionTimeoutMillis = String.valueOf(timeoutMillis); return this; } /** - * Same as {@link #responseTimeout(int)} but allowing to use JMeter + * Same as {@link #connectionTimeout(int)} but allowing to use JMeter * expressions * (variables or * functions) to solve the actual parameter values. * - * @param timeout a JMeter expression that returns timeout in milliseconds + * @param timeoutMillis a JMeter expression that returns timeout in milliseconds + * (default value is 20000 milliseconds) * @return the sampler for further configuration or usage * @since 2.2 */ - public DslConnectSampler responseTimeout(String timeout) { - this.responseTimeout = timeout; + public DslConnectSampler connectionTimeout(String timeoutMillis) { + this.connectionTimeoutMillis = timeoutMillis; return this; } /** * Sets the response timeout for the WebSocket negotiation. * - * @param timeout the response timeout in milliseconds - * @return the sampler for further configuration or usage - * @since 2.2 - */ - public DslConnectSampler responseTimeout(int timeout) { - this.responseTimeout = String.valueOf(timeout); - return this; - } - - /** - * Specifies the WebSocket server to connect to. - * - * @param server the WebSocket server to connect to - * @return the sampler for further configuration or usage - * @since 2.2 - */ - public DslConnectSampler server(String server) { - this.server = server; - return this; - } - - /** - * Specifies the WebSocket port to connect to. - * - * @param port the WebSocket port to connect to + * @param timeoutMillis the response timeout in milliseconds (default value is + * 6000 milliseconds) * @return the sampler for further configuration or usage * @since 2.2 */ - public DslConnectSampler port(String port) { - this.port = port; + public DslConnectSampler responseTimeout(int timeoutMillis) { + this.responseTimeoutMillis = String.valueOf(timeoutMillis); return this; } /** - * Specifies the WebSocket port to connect to. - * - * @param port the WebSocket port to connect to - * @return the sampler for further configuration or usage - * @since 2.2 - */ - public DslConnectSampler port(int port) { - this.port = String.valueOf(port); - return this; - } - - /** - * Specifies the WebSocket path to connect to. In case of need query parameters, - * they should be included in the path. - *

- * Example: + * Same as {@link #responseTimeout(int)} but allowing to use JMeter expressions + * (variables or functions) to solve the actual parameter values. * - *

{@code
-     * path("/websocket?room=general")
-     * }
- * - * @param path the WebSocket path to connect to - * @return the sampler for further configuration or usage - * @since 2.2 - */ - public DslConnectSampler path(String path) { - this.path = path; - return this; - } - - /** - * Specifies if the WebSocket connection should be established using TLS. - * - * @param tls the WebSocket TLS flag + * @param timeoutMillis a JMeter expression that returns timeout in milliseconds + * (default value is 6000 milliseconds) * @return the sampler for further configuration or usage * @since 2.2 */ - public DslConnectSampler tls(boolean tls) { - this.tls = tls; + public DslConnectSampler responseTimeout(String timeoutMillis) { + this.responseTimeoutMillis = timeoutMillis; return this; } @@ -382,35 +293,23 @@ public CodeBuilder(List builderMethods) { protected MethodCall buildMethodCall(OpenWebSocketSampler testElement, MethodCallContext context) { TestElementParamBuilder paramBuilder = new TestElementParamBuilder(testElement); - MethodParam server = paramBuilder.stringParam("server"); - MethodParam port = paramBuilder.stringParam("port"); - MethodParam path = paramBuilder.stringParam("path"); + MethodParam server = paramBuilder.stringParam("server", ""); + MethodParam port = paramBuilder.stringParam("port", ""); + MethodParam path = paramBuilder.stringParam("path", ""); MethodParam tls = paramBuilder.boolParam("TLS", false); - MethodCall ret = null; - if (!server.isDefault() && !port.isDefault() && !path.isDefault()) { - String protocol = tls.isDefault() ? "ws" - : (tls.getExpression().equals("true") ? "wss" : "ws"); - String url = protocol + "://" + server.getExpression() + ":" - + port.getExpression() + path.getExpression(); - ret = new MethodCall("webSocketSampler().connect", - DslConnectSampler.class, new StringParam(url)); - } else { - ret = buildMethodCall() - .chain("webSocketSampler().connect()") - .chain("server", server) - .chain("port", port) - .chain("path", path) - .chain("tls", tls); - } - return ret - .chain("connectionTimeout", paramBuilder.stringParam("connectTimeout", "")) - .chain("responseTimeout", paramBuilder.stringParam("readTimeout", "")); + String protocol = tls.getExpression().equals("true") ? "wss" : "ws"; + String url = protocol + "://" + server.getExpression() + ":" + port.getExpression() + + path.getExpression(); + return new MethodCall("DslWebsocketFactory.websocketConnect", DslConnectSampler.class, + new StringParam(url)) + .chain("connectionTimeout", paramBuilder.intParam("connectTimeout", 20000)) + .chain("responseTimeout", paramBuilder.intParam("readTimeout", 6000)); } } } public static class DslDisconnectSampler extends BaseSampler { - private String responseTimeout; + private String responseTimeoutMillis; private String statusCode; private DslDisconnectSampler() { @@ -420,8 +319,8 @@ private DslDisconnectSampler() { @Override protected TestElement buildTestElement() { CloseWebSocketSampler close = new CloseWebSocketSampler(); - if (responseTimeout != null) { - close.setReadTimeout(responseTimeout); + if (responseTimeoutMillis != null) { + close.setReadTimeout(responseTimeoutMillis); } if (statusCode != null) { close.setStatusCode(statusCode); @@ -429,46 +328,30 @@ protected TestElement buildTestElement() { return close; } - /** - * Same as {@link #responseTimeout(int)} but allowing to use JMeter - * expressions - * (variables or - * functions) to solve the actual parameter values. - * - * @param timeout a JMeter expression that returns timeout in milliseconds - * @return the sampler for further configuration or usage - * @since 2.2 - */ - public DslDisconnectSampler responseTimeout(String timeout) { - this.responseTimeout = timeout; - return this; - } - /** * Sets the response timeout for the close frame from server to be received. * - * @param timeout the response timeout in milliseconds + * @param timeoutMillis the response timeout in milliseconds (default value is + * 6000 milliseconds) * @return the sampler for further configuration or usage * @since 2.2 */ - public DslDisconnectSampler responseTimeout(int timeout) { - this.responseTimeout = String.valueOf(timeout); + public DslDisconnectSampler responseTimeout(int timeoutMillis) { + this.responseTimeoutMillis = String.valueOf(timeoutMillis); return this; } /** - * Same as {@link #statusCode(int)} but allowing to use JMeter - * expressions - * (variables or - * functions) to solve the actual parameter values. + * Same as {@link #responseTimeout(int)} but allowing to use JMeter expressions + * (variables or functions) to solve the actual parameter values. * - * @param statusCode a JMeter expression that returns the status code for the - * WebSocket disconnect + * @param timeoutMillis a JMeter expression that returns timeout in milliseconds + * (default value is 6000 milliseconds) * @return the sampler for further configuration or usage * @since 2.2 */ - public DslDisconnectSampler statusCode(String statusCode) { - this.statusCode = statusCode; + public DslDisconnectSampler responseTimeout(String timeoutMillis) { + this.responseTimeoutMillis = timeoutMillis; return this; } @@ -491,8 +374,23 @@ public DslDisconnectSampler statusCode(String statusCode) { * @return the sampler for further configuration or usage * @since 2.2 */ - public DslDisconnectSampler statusCode(int statusCode) { - this.statusCode = String.valueOf(statusCode); + public DslDisconnectSampler statusCode(StatusCode statusCode) { + this.statusCode = statusCode.propertyValue(); + return this; + } + + /** + * Same as {@link #statusCode(StatusCode)} but allowing to use JMeter + * expressions + * (variables or functions) to solve the actual parameter values. + * + * @param statusCode a JMeter expression that returns the status code for the + * WebSocket disconnect + * @return the sampler for further configuration or usage + * @since 2.2 + */ + public DslDisconnectSampler statusCode(String statusCode) { + this.statusCode = statusCode; return this; } @@ -506,9 +404,15 @@ public CodeBuilder(List builderMethods) { protected MethodCall buildMethodCall(CloseWebSocketSampler testElement, MethodCallContext context) { TestElementParamBuilder paramBuilder = new TestElementParamBuilder(testElement); - return new MethodCall("webSocketSampler().disconnect", DslDisconnectSampler.class) - .chain("responseTimeout", paramBuilder.stringParam("readTimeout")) - .chain("statusCode", paramBuilder.stringParam("statusCode")); + MethodParam statusCode; + try { + statusCode = paramBuilder.enumParam("statusCode", StatusCode.NORMAL_CLOSURE); + } catch (UnsupportedOperationException e) { + statusCode = paramBuilder.stringParam("statusCode", "1000"); + } + return new MethodCall("DslWebsocketFactory.websocketDisconnect", DslDisconnectSampler.class) + .chain("responseTimeout", paramBuilder.intParam("readTimeout", 6000)) + .chain("statusCode", statusCode); } } } @@ -516,10 +420,6 @@ protected MethodCall buildMethodCall(CloseWebSocketSampler testElement, public static class DslWriteSampler extends BaseSampler { private String requestData; - private DslWriteSampler() { - super("WebSocket Single Write", SingleWriteWebSocketSamplerGui.class); - } - private DslWriteSampler(String requestData) { super("WebSocket Single Write", SingleWriteWebSocketSamplerGui.class); this.requestData = requestData; @@ -534,18 +434,6 @@ protected TestElement buildTestElement() { return write; } - /** - * Sets the request data for the WebSocket write. - * - * @param requestData the request data for the WebSocket write - * @return the sampler for further configuration or usage - * @since 2.2 - */ - public DslWriteSampler requestData(String requestData) { - this.requestData = requestData; - return this; - } - public static class CodeBuilder extends SingleTestElementCallBuilder { @@ -557,15 +445,15 @@ public CodeBuilder(List builderMethods) { protected MethodCall buildMethodCall(SingleWriteWebSocketSampler testElement, MethodCallContext context) { TestElementParamBuilder paramBuilder = new TestElementParamBuilder(testElement); - return new MethodCall("webSocketSampler().write", DslWriteSampler.class) - .chain("requestData", paramBuilder.stringParam("requestData")) - .chain("createNewConnection", paramBuilder.boolParam("createNewConnection", false)); + MethodParam requestData = paramBuilder.stringParam("requestData", ""); + return new MethodCall("DslWebsocketFactory.websocketWrite", DslWriteSampler.class, + new StringParam(requestData.getExpression())); } } } public static class DslReadSampler extends BaseSampler { - private String responseTimeout; + private String responseTimeoutMillis; private boolean waitForResponse = true; private DslReadSampler() { @@ -575,8 +463,8 @@ private DslReadSampler() { @Override protected TestElement buildTestElement() { SingleReadWebSocketSampler read = new SingleReadWebSocketSampler(); - if (responseTimeout != null) { - read.setReadTimeout(responseTimeout); + if (responseTimeoutMillis != null) { + read.setReadTimeout(responseTimeoutMillis); } read.setDataType(DataType.Text); read.setOptional(!waitForResponse); @@ -603,29 +491,29 @@ public DslReadSampler waitForResponse(boolean waitForResponse) { } /** - * Same as {@link #responseTimeout(int)} but allowing to use JMeter - * expressions - * (variables or - * functions) to solve the actual parameter values. + * Same as {@link #responseTimeout(int)} but allowing to use JMeter expressions + * (variables or functions) to solve the actual parameter values. * - * @param timeout a JMeter expression that returns timeout in milliseconds + * @param timeoutMillis a JMeter expression that returns timeout in milliseconds + * (default value is 6000 milliseconds) * @return the sampler for further configuration or usage * @since 2.2 */ - public DslReadSampler responseTimeout(String timeout) { - this.responseTimeout = timeout; + public DslReadSampler responseTimeout(String timeoutMillis) { + this.responseTimeoutMillis = timeoutMillis; return this; } /** * Sets the response timeout for the WebSocket response to be received. * - * @param timeout the response timeout in milliseconds + * @param timeoutMillis the response timeout in milliseconds (default value is + * 6000 milliseconds) * @return the sampler for further configuration or usage * @since 2.2 */ - public DslReadSampler responseTimeout(int timeout) { - this.responseTimeout = String.valueOf(timeout); + public DslReadSampler responseTimeout(int timeoutMillis) { + this.responseTimeoutMillis = String.valueOf(timeoutMillis); return this; } @@ -640,8 +528,11 @@ public CodeBuilder(List builderMethods) { protected MethodCall buildMethodCall(SingleReadWebSocketSampler testElement, MethodCallContext context) { TestElementParamBuilder paramBuilder = new TestElementParamBuilder(testElement); - return new MethodCall("webSocketSampler().read", DslReadSampler.class) - .chain("responseTimeout", paramBuilder.stringParam("readTimeout", "")) + boolean optionalParam = !paramBuilder.boolParam("optional", false) + .getExpression().equals("true"); + return new MethodCall("DslWebsocketFactory.websocketRead", DslReadSampler.class) + .chain("responseTimeout", paramBuilder.intParam("readTimeout", 6000)) + .chain("waitForResponse", new BoolParam(optionalParam, true)) .chain("createNewConnection", paramBuilder.boolParam("createNewConnection", false)); } } diff --git a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java index 0a816f93..508bf883 100644 --- a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java +++ b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java @@ -1,9 +1,10 @@ +import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static us.abstracta.jmeter.javadsl.JmeterDsl.responseAssertion; import static us.abstracta.jmeter.javadsl.JmeterDsl.testPlan; import static us.abstracta.jmeter.javadsl.JmeterDsl.threadGroup; -import static us.abstracta.jmeter.javadsl.websocket.DslWebsocketSampler.webSocketSampler; +import us.abstracta.jmeter.javadsl.websocket.DslWebsocketFactory; import org.junit.jupiter.api.Test; import us.abstracta.jmeter.javadsl.core.TestPlanStats; @@ -12,26 +13,19 @@ public class DslWebsocketSamplerTest { @Test public void shouldConnectAndEchoMessageWhenWebSocketTestPlanWithEchoServer() throws Exception { - + WebSocketEchoServer echoServer = new WebSocketEchoServer(0); + echoServer.start(); + echoServer.awaitStart(5, TimeUnit.SECONDS); + String wsUri = echoServer.getUri(); TestPlanStats stats = testPlan( threadGroup(1, 1, - webSocketSampler() - .connect("wss://ws.postman-echo.com/raw") - .connectionTimeout("10000") - .responseTimeout("5000"), - webSocketSampler() - .write() - .requestData("Hello WebSocket Test!"), - webSocketSampler() - .read() - .responseTimeout("5000") + DslWebsocketFactory.websocketConnect(wsUri), + DslWebsocketFactory.websocketWrite("Hello WebSocket Test!"), + DslWebsocketFactory.websocketRead() .children( responseAssertion() .containsSubstrings("Hello WebSocket Test!")), - webSocketSampler() - .disconnect() - .responseTimeout("1000") - .statusCode("1000"))) + DslWebsocketFactory.websocketDisconnect())) .run(); assertThat(stats.overall().errorsCount()).isEqualTo(0); } @@ -39,7 +33,7 @@ public void shouldConnectAndEchoMessageWhenWebSocketTestPlanWithEchoServer() thr @Test public void shouldThrowIllegalArgumentExceptionWhenConnectWithInvalidUrl() { assertThrows(IllegalArgumentException.class, () -> { - webSocketSampler().connect("http://localhost:80/test"); + DslWebsocketFactory.websocketConnect("http://localhost:80/test"); }); } @@ -47,10 +41,7 @@ public void shouldThrowIllegalArgumentExceptionWhenConnectWithInvalidUrl() { public void shouldErrorSamplerWhenConnectToUnavailableServer() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - webSocketSampler() - .connect("ws://localhost:9999/nonexistent") - .connectionTimeout("2000") - .responseTimeout("1000"))) + DslWebsocketFactory.websocketConnect("ws://localhost:9999/nonexistent"))) .run(); assertThat(stats.overall().errorsCount()).isEqualTo(1); } @@ -59,10 +50,10 @@ public void shouldErrorSamplerWhenConnectToUnavailableServer() throws Exception public void shouldErrorSamplerWhenConnectWithVeryShortTimeout() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - webSocketSampler() - .connect("ws://localhost:8080/test") - .connectionTimeout("1") - .responseTimeout("1"))) + DslWebsocketFactory + .websocketConnect("ws://localhost:8080/test") + .connectionTimeout(1) + .responseTimeout(1))) .run(); assertThat(stats.overall().errorsCount()).isEqualTo(1); } @@ -71,9 +62,7 @@ public void shouldErrorSamplerWhenConnectWithVeryShortTimeout() throws Exception public void shouldErrorSamplerWhenWriteOperationWhenNoPreviousConnection() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - webSocketSampler() - .write() - .requestData("Test message"))) + DslWebsocketFactory.websocketWrite("Test message"))) .run(); assertThat(stats.overall().errorsCount()).isEqualTo(1); } @@ -82,9 +71,8 @@ public void shouldErrorSamplerWhenWriteOperationWhenNoPreviousConnection() throw public void shouldErrorSamplerWhenReadOperationWhenNoPreviousConnection() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - webSocketSampler() - .read() - .responseTimeout("1000"))) + DslWebsocketFactory + .websocketRead())) .run(); assertThat(stats.overall().errorsCount()).isEqualTo(1); } @@ -93,10 +81,8 @@ public void shouldErrorSamplerWhenReadOperationWhenNoPreviousConnection() throws public void shouldErrorSamplerWhenDisconnectOperationWhenNoPreviousConnection() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - webSocketSampler() - .disconnect() - .responseTimeout("1000") - .statusCode("1000"))) + DslWebsocketFactory + .websocketDisconnect())) .run(); assertThat(stats.overall().errorsCount()).isEqualTo(1); } diff --git a/jmeter-java-dsl-websocket/src/test/java/WebSocketEchoServer.java b/jmeter-java-dsl-websocket/src/test/java/WebSocketEchoServer.java new file mode 100644 index 00000000..6337ba4f --- /dev/null +++ b/jmeter-java-dsl-websocket/src/test/java/WebSocketEchoServer.java @@ -0,0 +1,48 @@ +import java.net.InetSocketAddress; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import org.java_websocket.WebSocket; +import org.java_websocket.handshake.ClientHandshake; +import org.java_websocket.server.WebSocketServer; + +public class WebSocketEchoServer extends WebSocketServer { + + private final CountDownLatch startLatch = new CountDownLatch(1); + + public WebSocketEchoServer(int port) { + super(new InetSocketAddress(port)); + } + + @Override + public void onOpen(WebSocket conn, ClientHandshake handshake) { + } + + @Override + public void onClose(WebSocket conn, int code, String reason, boolean remote) { + } + + @Override + public void onMessage(WebSocket conn, String message) { + conn.send(message); + } + + @Override + public void onError(WebSocket conn, Exception ex) { + ex.printStackTrace(); + } + + @Override + public void onStart() { + startLatch.countDown(); + } + + public void awaitStart(long timeout, TimeUnit unit) throws InterruptedException { + if (!startLatch.await(timeout, unit)) { + throw new RuntimeException("WebSocket server failed to start within timeout"); + } + } + + public String getUri() { + return "ws://localhost:" + getPort(); + } +} \ No newline at end of file diff --git a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketCodeGeneratorTest.java b/jmeter-java-dsl-websocket/src/test/java/WebsocketCodeGeneratorTest.java similarity index 72% rename from jmeter-java-dsl-websocket/src/test/java/DslWebsocketCodeGeneratorTest.java rename to jmeter-java-dsl-websocket/src/test/java/WebsocketCodeGeneratorTest.java index bbd84ef6..12430089 100644 --- a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketCodeGeneratorTest.java +++ b/jmeter-java-dsl-websocket/src/test/java/WebsocketCodeGeneratorTest.java @@ -13,9 +13,9 @@ import us.abstracta.jmeter.javadsl.codegeneration.TestClassTemplate; import us.abstracta.jmeter.javadsl.core.util.StringTemplate; import us.abstracta.jmeter.javadsl.util.TestResource; -import us.abstracta.jmeter.javadsl.websocket.DslWebsocketSampler; +import us.abstracta.jmeter.javadsl.websocket.DslWebsocketFactory; -public class DslWebsocketCodeGeneratorTest { +public class WebsocketCodeGeneratorTest { private static final String RESOURCES_FOLDER = "websocket-codegeneration"; @@ -23,7 +23,7 @@ public class DslWebsocketCodeGeneratorTest { public void shouldGenerateExpectedCodeWhenSimpleWebSocketJmxIsProvided(@TempDir Path tempDir) throws Exception { File solvedTemplate = solveTemplateResource("websocket-test-plan.template.jmx", tempDir); - assertThat(new DslCodeGenerator().addBuildersFrom(DslWebsocketSampler.class).generateCodeFromJmx(solvedTemplate)) + assertThat(new DslCodeGenerator().addBuildersFrom(DslWebsocketFactory.class).generateCodeFromJmx(solvedTemplate)) .isEqualToNormalizingNewlines( solveTestClassTemplate(Collections.emptySet(), "SimpleWebSocketTest.java")); @@ -32,24 +32,19 @@ public void shouldGenerateExpectedCodeWhenSimpleWebSocketJmxIsProvided(@TempDir @Test public void shouldGenerateExpectedCodeWhenComplexWebSocketJmxIsProvided(@TempDir Path tempDir) throws Exception { File solvedTemplate = solveTemplateResource("/complex-websocket.jmx", tempDir); - assertThat(new DslCodeGenerator().addBuildersFrom(DslWebsocketSampler.class).generateCodeFromJmx(solvedTemplate)) - .isEqualToNormalizingNewlines(solveTestClassTemplate(Collections.emptySet(), - "ComplexWebSocketTest.java")); - } - - @Test - public void shouldGenerateExpectedCodeWhenWebSocketWithAssertionsJmxIsProvided(@TempDir Path tempDir) throws Exception { - File solvedTemplate = solveTemplateResource("/websocket-with-assertions.jmx", tempDir); - assertThat(new DslCodeGenerator().addBuildersFrom(DslWebsocketSampler.class).generateCodeFromJmx(solvedTemplate)) - .isEqualToNormalizingNewlines(solveTestClassTemplate(Collections.emptySet(), - "WebSocketWithAssertionsTest.java")); + assertThat(new DslCodeGenerator().addBuildersFrom(DslWebsocketFactory.class).generateCodeFromJmx(solvedTemplate)) + .isEqualToNormalizingNewlines( + solveTestClassTemplate(Collections.emptySet(), + "ComplexWebSocketTest.java")); } @Test - public void shouldGenerateExpectedCodeWhenWebSocketWithVariablesJmxIsProvided(@TempDir Path tempDir) throws Exception { + public void shouldGenerateExpectedCodeWhenWebSocketWithVariablesJmxIsProvided(@TempDir Path tempDir) + throws Exception { assertThat(new DslCodeGenerator() - .addBuildersFrom(DslWebsocketSampler.class) - .addDependency(DslWebsocketSampler.class, "us.abstracta.jmeter:jmeter-java-dsl-websocket") + + .addBuildersFrom(DslWebsocketFactory.class) + .addDependency(DslWebsocketFactory.class, "us.abstracta.jmeter:jmeter-java-dsl-websocket") .generateCodeFromJmx(new TestResource(RESOURCES_FOLDER + "/websocket-with-variables.jmx").file())) .isEqualToNormalizingNewlines(solveTestClassTemplate(Collections.emptySet(), "WebSocketWithVariablesTest.java")); diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/ComplexWebSocketTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/ComplexWebSocketTest.java index 8330ba5d..2c8979f5 100644 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/ComplexWebSocketTest.java +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/ComplexWebSocketTest.java @@ -1,14 +1,14 @@ testPlan( threadGroup(1, 1, - webSocketSampler().connect("ws://echo.websocket.org:80/") - .connectionTimeout("15000") - .responseTimeout("10000"), - webSocketSampler().write() - .requestData("Hello from JMeter WebSocket Test"), - webSocketSampler().read() - .responseTimeout("10000"), - webSocketSampler().disconnect() - .responseTimeout("2000") - .statusCode("1000") + DslWebsocketFactory.websocketConnect("ws://echo.websocket.org:80/") + .connectionTimeout(15000) + .responseTimeout(10000), + DslWebsocketFactory.websocketWrite("Hello from JMeter WebSocket Test"), + DslWebsocketFactory.websocketRead() + .responseTimeout(10000) + .waitForResponse(false), + DslWebsocketFactory.websocketDisconnect() + .responseTimeout(2000) + .statusCode("3000") ) ) \ No newline at end of file diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/SimpleWebSocketTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/SimpleWebSocketTest.java index 2384ef75..4fcf4796 100644 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/SimpleWebSocketTest.java +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/SimpleWebSocketTest.java @@ -1,14 +1,8 @@ testPlan( threadGroup(1, 1, - webSocketSampler().connect("ws://ws.postman-echo.com:80/raw") - .connectionTimeout("10000") - .responseTimeout("5000"), - webSocketSampler().write() - .requestData("Hello WebSocket Test!"), - webSocketSampler().read() - .responseTimeout("5000"), - webSocketSampler().disconnect() - .responseTimeout("1000") - .statusCode("1000") + DslWebsocketFactory.websocketConnect("ws://ws.postman-echo.com:80/raw"), + DslWebsocketFactory.websocketWrite("Hello WebSocket Test!"), + DslWebsocketFactory.websocketRead(), + DslWebsocketFactory.websocketDisconnect() ) ) \ No newline at end of file diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithAssertionsTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithAssertionsTest.java deleted file mode 100644 index 3396b495..00000000 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithAssertionsTest.java +++ /dev/null @@ -1,18 +0,0 @@ -testPlan( - threadGroup(1, 1, - webSocketSampler().connect("ws://ws.postman-echo.com:80/raw") - .connectionTimeout("10000") - .responseTimeout("5000"), - webSocketSampler().write() - .requestData("Test message with assertions"), - webSocketSampler().read() - .responseTimeout("5000") - .children( - responseAssertion() - .containsRegexes("Test message with assertions") - ), - webSocketSampler().disconnect() - .responseTimeout("1000") - .statusCode("1000") - ) - ) \ No newline at end of file diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithVariablesTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithVariablesTest.java index 29f055f1..2a6ef466 100644 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithVariablesTest.java +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithVariablesTest.java @@ -1,18 +1,13 @@ testPlan( - vars() - .set("WEBSOCKET_SERVER", "ws.postman-echo.com") - .set("WEBSOCKET_PORT", "80") - .set("MESSAGE", "Hello from variable"), threadGroup(1, 1, - webSocketSampler().connect("ws://${WEBSOCKET_SERVER}:${WEBSOCKET_PORT}/raw") - .connectionTimeout("10000") - .responseTimeout("5000"), - webSocketSampler().write() - .requestData("${MESSAGE}"), - webSocketSampler().read() - .responseTimeout("5000"), - webSocketSampler().disconnect() - .responseTimeout("1000") - .statusCode("1000") + DslWebsocketFactory.websocketConnect("ws://${WEBSOCKET_SERVER}:${WEBSOCKET_PORT}/raw") + .connectionTimeout("${timeout}") + .responseTimeout("${timeout}"), + DslWebsocketFactory.websocketWrite("${MESSAGE}"), + DslWebsocketFactory.websocketRead() + .responseTimeout("${timeout}"), + DslWebsocketFactory.websocketDisconnect() + .responseTimeout("${timeout}") + .statusCode("${statusCode}") ) ) \ No newline at end of file diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/complex-websocket.jmx b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/complex-websocket.jmx index fc50f39d..ee7a1447 100644 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/complex-websocket.jmx +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/complex-websocket.jmx @@ -23,7 +23,7 @@ - + 15000 10000 false @@ -32,7 +32,7 @@ / - + 20000 Text Hello from JMeter WebSocket Test @@ -45,11 +45,11 @@ - + 20000 10000 Text - false + true false false @@ -57,9 +57,9 @@ - + 2000 - 1000 + 3000 diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-test-plan.template.jmx b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-test-plan.template.jmx index 7ce05446..7fede633 100644 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-test-plan.template.jmx +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-test-plan.template.jmx @@ -1,5 +1,5 @@ - + @@ -12,30 +12,27 @@ - + + 1 + 1 + true continue - - false + 1 + false - 1 - 1 - false - - - true - - 10000 - 5000 + + + false ws.postman-echo.com 80 /raw - + 20000 Text Hello WebSocket Test! @@ -48,9 +45,9 @@ - + 20000 - 5000 + Text false false @@ -60,9 +57,9 @@ - - 1000 - 1000 + + + diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-with-assertions.jmx b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-with-assertions.jmx deleted file mode 100644 index 0fb1f417..00000000 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-with-assertions.jmx +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - false - true - false - - - - - - - - continue - - false - 1 - - 1 - 1 - false - - - true - - - - 10000 - 5000 - false - ws.postman-echo.com - 80 - /raw - - - - 20000 - Text - Test message with assertions - false - false - - 80 - - false - - - - - 20000 - 5000 - Text - false - false - false - - 80 - - - - - - Test message with assertions - - - Assertion.response_data - false - 2 - - - - - 1000 - 1000 - - - - - - diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-with-variables.jmx b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-with-variables.jmx index 332d7b25..776f6ad8 100644 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-with-variables.jmx +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/websocket-with-variables.jmx @@ -22,29 +22,11 @@ + false + false - - - - WEBSOCKET_SERVER - ws.postman-echo.com - = - - - WEBSOCKET_PORT - 80 - = - - - MESSAGE - Hello from variable - = - - - - - + 1 1 true @@ -56,8 +38,8 @@ - 10000 - 5000 + ${timeout} + ${timeout} false ${WEBSOCKET_SERVER} ${WEBSOCKET_PORT} @@ -79,7 +61,7 @@ 20000 - 5000 + ${timeout} Text false false @@ -90,8 +72,8 @@ - 1000 - 1000 + ${timeout} + ${statusCode} From 97da1c46c0adeda1d14d687646f43afc9226f929 Mon Sep 17 00:00:00 2001 From: Sebastian Lorenzo Date: Tue, 9 Dec 2025 17:20:49 -0300 Subject: [PATCH 11/11] Change websocket factory name and change the way to check status code enum in code generation --- docs/guide/protocols/websocket.md | 20 +++--- .../jmeter/javadsl/cli/Jmx2DslCommand.java | 4 +- ...etFactory.java => WebsocketJMeterDsl.java} | 71 ++++++++++--------- .../test/java/DslWebsocketSamplerTest.java | 25 +++---- .../test/java/WebsocketCodeGeneratorTest.java | 10 +-- .../ComplexWebSocketTest.java | 8 +-- .../SimpleWebSocketTest.java | 8 +-- .../WebSocketWithVariablesTest.java | 8 +-- .../complex-websocket.jmx | 12 ++-- 9 files changed, 82 insertions(+), 84 deletions(-) rename jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/{DslWebsocketFactory.java => WebsocketJMeterDsl.java} (96%) diff --git a/docs/guide/protocols/websocket.md b/docs/guide/protocols/websocket.md index 1428615f..b62c4a63 100644 --- a/docs/guide/protocols/websocket.md +++ b/docs/guide/protocols/websocket.md @@ -24,25 +24,25 @@ testImplementation 'us.abstracta.jmeter:jmeter-java-dsl-websocket:2.2' -Following you can see a basic usage example of Web Socket protocol. +Below you can see a basic usage example of WebSocket protocol. ```java import static us.abstracta.jmeter.javadsl.JmeterDsl.*; -import us.abstracta.jmeter.javadsl.websocket.DslWebsocketFactory; +import static us.abstracta.jmeter.javadsl.websocket.WebsocketJMeterDsl.*; import us.abstracta.jmeter.javadsl.core.TestPlanStats; public class Test { public static void main(String[] args) throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - DslWebsocketFactory.websocketConnect("wss://ws.postman-echo.com/raw"), - DslWebsocketFactory.websocketWrite("Hello WebSocket!"), - DslWebsocketFactory.websocketRead() + websocketConnect("wss://ws.postman-echo.com/raw"), + websocketWrite("Hello WebSocket!"), + websocketRead() .children( responseAssertion() .equalsToStrings("Hello WebSocket!") ), - DslWebsocketFactory.websocketDisconnect() + websocketDisconnect() ) ).run(); } @@ -54,15 +54,15 @@ Only `ws://` and `wss://` protocols are supported. Using any other scheme will t ::: ::: tip -You can use a non blocking read if it is necessary in the following way +You can use a non-blocking read if necessary in the following way: ```java -DslWebsocketFactory.websocketRead().waitForResponse(false) +websocketRead().waitForResponse(false) ``` -In this case is not recommended to add an assertion due the response could be empty +In this case, it is not recommended to add an assertion because the response could be empty. ::: ::: warning -Web Socket plugin only supports one connection for threads at a time. If you want to change Web Socket server during execution you should add a disconnect sampler and then establish a new connection. +The WebSocket plugin only supports one connection per thread at a time. If you want to change the WebSocket server during execution, you should add a disconnect sampler and then establish a new connection. ::: \ No newline at end of file diff --git a/jmeter-java-dsl-cli/src/main/java/us/abstracta/jmeter/javadsl/cli/Jmx2DslCommand.java b/jmeter-java-dsl-cli/src/main/java/us/abstracta/jmeter/javadsl/cli/Jmx2DslCommand.java index 6df9fad3..744f14be 100644 --- a/jmeter-java-dsl-cli/src/main/java/us/abstracta/jmeter/javadsl/cli/Jmx2DslCommand.java +++ b/jmeter-java-dsl-cli/src/main/java/us/abstracta/jmeter/javadsl/cli/Jmx2DslCommand.java @@ -11,7 +11,7 @@ import us.abstracta.jmeter.javadsl.graphql.DslGraphqlSampler; import us.abstracta.jmeter.javadsl.jdbc.JdbcJmeterDsl; import us.abstracta.jmeter.javadsl.parallel.ParallelController; -import us.abstracta.jmeter.javadsl.websocket.DslWebsocketFactory; +import us.abstracta.jmeter.javadsl.websocket.WebsocketJMeterDsl; import us.abstracta.jmeter.javadsl.wrapper.WrapperJmeterDsl; @Command(name = "jmx2dsl", header = "Converts a JMX file to DSL code", @@ -49,7 +49,7 @@ public Integer call() throws Exception { addBuildersFrom(ElasticsearchBackendListener.class, "jmeter-java-dsl-elasticsearch-listener", codeGenerator); addBuildersFrom(DatadogBackendListener.class, "jmeter-java-dsl-datadog", codeGenerator); - addBuildersFrom(DslWebsocketFactory.class, "jmeter-java-dsl-websocket", codeGenerator); + addBuildersFrom(WebsocketJMeterDsl.class, "jmeter-java-dsl-websocket", codeGenerator); System.out.println(codeGenerator.generateCodeFromJmx(jmxFile)); return 0; } diff --git a/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketFactory.java b/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/WebsocketJMeterDsl.java similarity index 96% rename from jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketFactory.java rename to jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/WebsocketJMeterDsl.java index b71a73dd..1a82411e 100644 --- a/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/DslWebsocketFactory.java +++ b/jmeter-java-dsl-websocket/src/main/java/us/abstracta/jmeter/javadsl/websocket/WebsocketJMeterDsl.java @@ -13,6 +13,7 @@ import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; +import java.util.Arrays; import java.util.List; import org.apache.jmeter.testelement.TestElement; import us.abstracta.jmeter.javadsl.codegeneration.MethodCall; @@ -65,9 +66,9 @@ * * @since 2.2 */ -public class DslWebsocketFactory { +public class WebsocketJMeterDsl { - private DslWebsocketFactory() { + private WebsocketJMeterDsl() { } /** @@ -139,29 +140,6 @@ public static DslReadSampler websocketRead() { return new DslReadSampler(); } - public enum StatusCode implements EnumParam.EnumPropertyValue { - NORMAL_CLOSURE("1000"), - GOING_AWAY("1001"), - PROTOCOL_ERROR("1002"), - UNSUPPORTED_DATA("1003"), - NO_STATUS_CODE_PRESENT("1005"), - MESSAGE_TYPE_ERROR("1007"), - POLICY_VIOLATION("1008"), - MESSAGE_TOO_BIG_ERROR("1009"), - TLS_HANDSHAKE_ERROR("1015"); - - private final String propertyValue; - - StatusCode(String propertyValue) { - this.propertyValue = propertyValue; - } - - @Override - public String propertyValue() { - return propertyValue; - } - } - public static class DslConnectSampler extends BaseSampler { private String connectionTimeoutMillis; private String responseTimeoutMillis; @@ -300,7 +278,7 @@ protected MethodCall buildMethodCall(OpenWebSocketSampler testElement, String protocol = tls.getExpression().equals("true") ? "wss" : "ws"; String url = protocol + "://" + server.getExpression() + ":" + port.getExpression() + path.getExpression(); - return new MethodCall("DslWebsocketFactory.websocketConnect", DslConnectSampler.class, + return new MethodCall("websocketConnect", DslConnectSampler.class, new StringParam(url)) .chain("connectionTimeout", paramBuilder.intParam("connectTimeout", 20000)) .chain("responseTimeout", paramBuilder.intParam("readTimeout", 6000)); @@ -308,6 +286,35 @@ protected MethodCall buildMethodCall(OpenWebSocketSampler testElement, } } + public enum StatusCode implements EnumParam.EnumPropertyValue { + NORMAL_CLOSURE("1000"), + GOING_AWAY("1001"), + PROTOCOL_ERROR("1002"), + UNSUPPORTED_DATA("1003"), + NO_STATUS_CODE_PRESENT("1005"), + MESSAGE_TYPE_ERROR("1007"), + POLICY_VIOLATION("1008"), + MESSAGE_TOO_BIG_ERROR("1009"), + TLS_HANDSHAKE_ERROR("1015"); + + private final String propertyValue; + + StatusCode(String propertyValue) { + this.propertyValue = propertyValue; + } + + @Override + public String propertyValue() { + return propertyValue; + } + + public static boolean isValidStatusCode(String value) { + return Arrays.stream(StatusCode.values()) + .anyMatch(code -> code.propertyValue().equals(value)); + } + + } + public static class DslDisconnectSampler extends BaseSampler { private String responseTimeoutMillis; private String statusCode; @@ -404,13 +411,11 @@ public CodeBuilder(List builderMethods) { protected MethodCall buildMethodCall(CloseWebSocketSampler testElement, MethodCallContext context) { TestElementParamBuilder paramBuilder = new TestElementParamBuilder(testElement); - MethodParam statusCode; - try { + MethodParam statusCode = paramBuilder.stringParam("statusCode", "1000"); + if (StatusCode.isValidStatusCode(statusCode.getExpression())) { statusCode = paramBuilder.enumParam("statusCode", StatusCode.NORMAL_CLOSURE); - } catch (UnsupportedOperationException e) { - statusCode = paramBuilder.stringParam("statusCode", "1000"); } - return new MethodCall("DslWebsocketFactory.websocketDisconnect", DslDisconnectSampler.class) + return new MethodCall("websocketDisconnect", DslDisconnectSampler.class) .chain("responseTimeout", paramBuilder.intParam("readTimeout", 6000)) .chain("statusCode", statusCode); } @@ -446,7 +451,7 @@ protected MethodCall buildMethodCall(SingleWriteWebSocketSampler testElement, MethodCallContext context) { TestElementParamBuilder paramBuilder = new TestElementParamBuilder(testElement); MethodParam requestData = paramBuilder.stringParam("requestData", ""); - return new MethodCall("DslWebsocketFactory.websocketWrite", DslWriteSampler.class, + return new MethodCall("websocketWrite", DslWriteSampler.class, new StringParam(requestData.getExpression())); } } @@ -530,7 +535,7 @@ protected MethodCall buildMethodCall(SingleReadWebSocketSampler testElement, TestElementParamBuilder paramBuilder = new TestElementParamBuilder(testElement); boolean optionalParam = !paramBuilder.boolParam("optional", false) .getExpression().equals("true"); - return new MethodCall("DslWebsocketFactory.websocketRead", DslReadSampler.class) + return new MethodCall("websocketRead", DslReadSampler.class) .chain("responseTimeout", paramBuilder.intParam("readTimeout", 6000)) .chain("waitForResponse", new BoolParam(optionalParam, true)) .chain("createNewConnection", paramBuilder.boolParam("createNewConnection", false)); diff --git a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java index 508bf883..a1122587 100644 --- a/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java +++ b/jmeter-java-dsl-websocket/src/test/java/DslWebsocketSamplerTest.java @@ -4,7 +4,7 @@ import static us.abstracta.jmeter.javadsl.JmeterDsl.responseAssertion; import static us.abstracta.jmeter.javadsl.JmeterDsl.testPlan; import static us.abstracta.jmeter.javadsl.JmeterDsl.threadGroup; -import us.abstracta.jmeter.javadsl.websocket.DslWebsocketFactory; +import static us.abstracta.jmeter.javadsl.websocket.WebsocketJMeterDsl.*; import org.junit.jupiter.api.Test; import us.abstracta.jmeter.javadsl.core.TestPlanStats; @@ -19,13 +19,13 @@ public void shouldConnectAndEchoMessageWhenWebSocketTestPlanWithEchoServer() thr String wsUri = echoServer.getUri(); TestPlanStats stats = testPlan( threadGroup(1, 1, - DslWebsocketFactory.websocketConnect(wsUri), - DslWebsocketFactory.websocketWrite("Hello WebSocket Test!"), - DslWebsocketFactory.websocketRead() + websocketConnect(wsUri), + websocketWrite("Hello WebSocket Test!"), + websocketRead() .children( responseAssertion() .containsSubstrings("Hello WebSocket Test!")), - DslWebsocketFactory.websocketDisconnect())) + websocketDisconnect())) .run(); assertThat(stats.overall().errorsCount()).isEqualTo(0); } @@ -33,7 +33,7 @@ public void shouldConnectAndEchoMessageWhenWebSocketTestPlanWithEchoServer() thr @Test public void shouldThrowIllegalArgumentExceptionWhenConnectWithInvalidUrl() { assertThrows(IllegalArgumentException.class, () -> { - DslWebsocketFactory.websocketConnect("http://localhost:80/test"); + websocketConnect("http://localhost:80/test"); }); } @@ -41,7 +41,7 @@ public void shouldThrowIllegalArgumentExceptionWhenConnectWithInvalidUrl() { public void shouldErrorSamplerWhenConnectToUnavailableServer() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - DslWebsocketFactory.websocketConnect("ws://localhost:9999/nonexistent"))) + websocketConnect("ws://localhost:9999/nonexistent"))) .run(); assertThat(stats.overall().errorsCount()).isEqualTo(1); } @@ -50,8 +50,7 @@ public void shouldErrorSamplerWhenConnectToUnavailableServer() throws Exception public void shouldErrorSamplerWhenConnectWithVeryShortTimeout() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - DslWebsocketFactory - .websocketConnect("ws://localhost:8080/test") + websocketConnect("ws://localhost:8080/test") .connectionTimeout(1) .responseTimeout(1))) .run(); @@ -62,7 +61,7 @@ public void shouldErrorSamplerWhenConnectWithVeryShortTimeout() throws Exception public void shouldErrorSamplerWhenWriteOperationWhenNoPreviousConnection() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - DslWebsocketFactory.websocketWrite("Test message"))) + websocketWrite("Test message"))) .run(); assertThat(stats.overall().errorsCount()).isEqualTo(1); } @@ -71,8 +70,7 @@ public void shouldErrorSamplerWhenWriteOperationWhenNoPreviousConnection() throw public void shouldErrorSamplerWhenReadOperationWhenNoPreviousConnection() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - DslWebsocketFactory - .websocketRead())) + websocketRead())) .run(); assertThat(stats.overall().errorsCount()).isEqualTo(1); } @@ -81,8 +79,7 @@ public void shouldErrorSamplerWhenReadOperationWhenNoPreviousConnection() throws public void shouldErrorSamplerWhenDisconnectOperationWhenNoPreviousConnection() throws Exception { TestPlanStats stats = testPlan( threadGroup(1, 1, - DslWebsocketFactory - .websocketDisconnect())) + websocketDisconnect())) .run(); assertThat(stats.overall().errorsCount()).isEqualTo(1); } diff --git a/jmeter-java-dsl-websocket/src/test/java/WebsocketCodeGeneratorTest.java b/jmeter-java-dsl-websocket/src/test/java/WebsocketCodeGeneratorTest.java index 12430089..9dd3d1bd 100644 --- a/jmeter-java-dsl-websocket/src/test/java/WebsocketCodeGeneratorTest.java +++ b/jmeter-java-dsl-websocket/src/test/java/WebsocketCodeGeneratorTest.java @@ -13,7 +13,7 @@ import us.abstracta.jmeter.javadsl.codegeneration.TestClassTemplate; import us.abstracta.jmeter.javadsl.core.util.StringTemplate; import us.abstracta.jmeter.javadsl.util.TestResource; -import us.abstracta.jmeter.javadsl.websocket.DslWebsocketFactory; +import us.abstracta.jmeter.javadsl.websocket.WebsocketJMeterDsl; public class WebsocketCodeGeneratorTest { @@ -23,7 +23,7 @@ public class WebsocketCodeGeneratorTest { public void shouldGenerateExpectedCodeWhenSimpleWebSocketJmxIsProvided(@TempDir Path tempDir) throws Exception { File solvedTemplate = solveTemplateResource("websocket-test-plan.template.jmx", tempDir); - assertThat(new DslCodeGenerator().addBuildersFrom(DslWebsocketFactory.class).generateCodeFromJmx(solvedTemplate)) + assertThat(new DslCodeGenerator().addBuildersFrom(WebsocketJMeterDsl.class).generateCodeFromJmx(solvedTemplate)) .isEqualToNormalizingNewlines( solveTestClassTemplate(Collections.emptySet(), "SimpleWebSocketTest.java")); @@ -32,7 +32,7 @@ public void shouldGenerateExpectedCodeWhenSimpleWebSocketJmxIsProvided(@TempDir @Test public void shouldGenerateExpectedCodeWhenComplexWebSocketJmxIsProvided(@TempDir Path tempDir) throws Exception { File solvedTemplate = solveTemplateResource("/complex-websocket.jmx", tempDir); - assertThat(new DslCodeGenerator().addBuildersFrom(DslWebsocketFactory.class).generateCodeFromJmx(solvedTemplate)) + assertThat(new DslCodeGenerator().addBuildersFrom(WebsocketJMeterDsl.class).generateCodeFromJmx(solvedTemplate)) .isEqualToNormalizingNewlines( solveTestClassTemplate(Collections.emptySet(), "ComplexWebSocketTest.java")); @@ -43,8 +43,8 @@ public void shouldGenerateExpectedCodeWhenWebSocketWithVariablesJmxIsProvided(@T throws Exception { assertThat(new DslCodeGenerator() - .addBuildersFrom(DslWebsocketFactory.class) - .addDependency(DslWebsocketFactory.class, "us.abstracta.jmeter:jmeter-java-dsl-websocket") + .addBuildersFrom(WebsocketJMeterDsl.class) + .addDependency(WebsocketJMeterDsl.class, "us.abstracta.jmeter:jmeter-java-dsl-websocket") .generateCodeFromJmx(new TestResource(RESOURCES_FOLDER + "/websocket-with-variables.jmx").file())) .isEqualToNormalizingNewlines(solveTestClassTemplate(Collections.emptySet(), "WebSocketWithVariablesTest.java")); diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/ComplexWebSocketTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/ComplexWebSocketTest.java index 2c8979f5..48647b41 100644 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/ComplexWebSocketTest.java +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/ComplexWebSocketTest.java @@ -1,13 +1,13 @@ testPlan( threadGroup(1, 1, - DslWebsocketFactory.websocketConnect("ws://echo.websocket.org:80/") + websocketConnect("ws://echo.websocket.org:80/") .connectionTimeout(15000) .responseTimeout(10000), - DslWebsocketFactory.websocketWrite("Hello from JMeter WebSocket Test"), - DslWebsocketFactory.websocketRead() + websocketWrite("Hello from JMeter WebSocket Test"), + websocketRead() .responseTimeout(10000) .waitForResponse(false), - DslWebsocketFactory.websocketDisconnect() + websocketDisconnect() .responseTimeout(2000) .statusCode("3000") ) diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/SimpleWebSocketTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/SimpleWebSocketTest.java index 4fcf4796..ea6f7bc2 100644 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/SimpleWebSocketTest.java +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/SimpleWebSocketTest.java @@ -1,8 +1,8 @@ testPlan( threadGroup(1, 1, - DslWebsocketFactory.websocketConnect("ws://ws.postman-echo.com:80/raw"), - DslWebsocketFactory.websocketWrite("Hello WebSocket Test!"), - DslWebsocketFactory.websocketRead(), - DslWebsocketFactory.websocketDisconnect() + websocketConnect("ws://ws.postman-echo.com:80/raw"), + websocketWrite("Hello WebSocket Test!"), + websocketRead(), + websocketDisconnect() ) ) \ No newline at end of file diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithVariablesTest.java b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithVariablesTest.java index 2a6ef466..7f46b58c 100644 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithVariablesTest.java +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/WebSocketWithVariablesTest.java @@ -1,12 +1,12 @@ testPlan( threadGroup(1, 1, - DslWebsocketFactory.websocketConnect("ws://${WEBSOCKET_SERVER}:${WEBSOCKET_PORT}/raw") + websocketConnect("ws://${WEBSOCKET_SERVER}:${WEBSOCKET_PORT}/raw") .connectionTimeout("${timeout}") .responseTimeout("${timeout}"), - DslWebsocketFactory.websocketWrite("${MESSAGE}"), - DslWebsocketFactory.websocketRead() + websocketWrite("${MESSAGE}"), + websocketRead() .responseTimeout("${timeout}"), - DslWebsocketFactory.websocketDisconnect() + websocketDisconnect() .responseTimeout("${timeout}") .statusCode("${statusCode}") ) diff --git a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/complex-websocket.jmx b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/complex-websocket.jmx index ee7a1447..17ce5036 100644 --- a/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/complex-websocket.jmx +++ b/jmeter-java-dsl-websocket/src/test/resources/websocket-codegeneration/complex-websocket.jmx @@ -1,15 +1,11 @@ - - - false + true - false - + - @@ -23,7 +19,7 @@ - + 15000 10000 false @@ -32,7 +28,7 @@ / - + 20000 Text Hello from JMeter WebSocket Test