Skip to content

Commit 4fd140d

Browse files
Thorsten Schlathoelterbbortt
Thorsten Schlathoelter
authored andcommitted
feat(#1156): provide test api generator
1 parent bd5e248 commit 4fd140d

File tree

115 files changed

+12632
-60
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

115 files changed

+12632
-60
lines changed

Diff for: core/citrus-api/src/main/java/org/citrusframework/message/MessagePayloadUtils.java

+34
Original file line numberDiff line numberDiff line change
@@ -212,4 +212,38 @@ public static String prettyPrintJson(String payload) {
212212
}
213213
return sb.toString();
214214
}
215+
216+
/**
217+
* Normalizes the given text by replacing all whitespace characters (identified by {@link Character#isWhitespace) by a single space
218+
* and replacing windows style line endings with unix style line endings.
219+
*/
220+
public static String normalizeWhitespace(String text, boolean normalizeWhitespace, boolean normalizeLineEndingsToUnix) {
221+
if (text == null || text.isEmpty()) {
222+
return text;
223+
}
224+
225+
if (normalizeWhitespace) {
226+
StringBuilder result = new StringBuilder();
227+
boolean lastWasSpace = true;
228+
for (int i = 0; i < text.length(); i++) {
229+
char c = text.charAt(i);
230+
if (Character.isWhitespace(c)) {
231+
if (!lastWasSpace) {
232+
result.append(' ');
233+
}
234+
lastWasSpace = true;
235+
} else {
236+
result.append(c);
237+
lastWasSpace = false;
238+
}
239+
}
240+
return result.toString().trim();
241+
}
242+
243+
if (normalizeLineEndingsToUnix) {
244+
return text.replaceAll("\\r(\\n)?", "\n");
245+
}
246+
247+
return text;
248+
}
215249
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.citrusframework.testapi;
2+
3+
import java.util.Map;
4+
5+
/**
6+
* Interface representing a generated API from an OpenAPI specification.
7+
* Provides methods to retrieve metadata about the API such as title, version,
8+
* prefix, and information extensions.
9+
*/
10+
public interface GeneratedApi {
11+
12+
/**
13+
* Retrieves the title of the OpenAPI specification, as specified in the info section of the API.
14+
*
15+
* @return the title of the OpenAPI specification
16+
*/
17+
String getApiTitle();
18+
19+
/**
20+
* Retrieves the version of the OpenAPI specification, as specified in the info section of the API.
21+
*
22+
* @return the version of the OpenAPI specification
23+
*/
24+
String getApiVersion();
25+
26+
/**
27+
* Retrieves the prefix used for the API, as specified in the API generation configuration.
28+
*
29+
* @return the prefix used for the API
30+
*/
31+
String getApiPrefix();
32+
33+
/**
34+
* Retrieves the specification extensions of the OpenAPI defined in the "info" section.
35+
* <p>
36+
* Specification extensions, also known as vendor extensions, are custom key-value pairs used to describe extra
37+
* functionality not covered by the standard OpenAPI Specification. These properties start with "x-".
38+
* This method collects only the extensions defined in the "info" section of the API.
39+
* </p>
40+
*
41+
* @return a map containing the specification extensions defined in the "info" section of the API,
42+
* where keys are extension names and values are extension values
43+
*/
44+
Map<String, String> getApiInfoExtensions();
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.citrusframework.testapi;
2+
3+
/**
4+
* Interface representing a generated API request corresponding to an operation in an OpenAPI specification.
5+
* Provides methods to retrieve metadata about the request such as operation name, HTTP method, and path.
6+
*/
7+
public interface GeneratedApiRequest {
8+
9+
/**
10+
* Retrieves the name of the OpenAPI operation associated with the request.
11+
*
12+
* @return the name of the OpenAPI operation
13+
*/
14+
String getOperationName();
15+
16+
/**
17+
* Retrieves the HTTP method used for the request.
18+
*
19+
* @return the HTTP method used for the request (e.g., GET, POST)
20+
*/
21+
String getMethod();
22+
23+
/**
24+
* Retrieves the path used for the request.
25+
*
26+
* @return the path used for the request
27+
*/
28+
String getPath();
29+
}

Diff for: core/citrus-api/src/test/java/org/citrusframework/message/MessagePayloadUtilsTest.java

+45-22
Original file line numberDiff line numberDiff line change
@@ -16,56 +16,79 @@
1616

1717
package org.citrusframework.message;
1818

19+
import static org.testng.Assert.assertEquals;
20+
1921
import org.testng.Assert;
22+
import org.testng.annotations.DataProvider;
2023
import org.testng.annotations.Test;
2124

2225
public class MessagePayloadUtilsTest {
2326

2427
@Test
2528
public void shouldPrettyPrintJson() {
26-
Assert.assertEquals(MessagePayloadUtils.prettyPrint(""), "");
27-
Assert.assertEquals(MessagePayloadUtils.prettyPrint("{}"), "{}");
28-
Assert.assertEquals(MessagePayloadUtils.prettyPrint("[]"), "[]");
29-
Assert.assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\"}"),
29+
assertEquals(MessagePayloadUtils.prettyPrint(""), "");
30+
assertEquals(MessagePayloadUtils.prettyPrint("{}"), "{}");
31+
assertEquals(MessagePayloadUtils.prettyPrint("[]"), "[]");
32+
assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\"}"),
3033
String.format("{%n \"user\": \"citrus\"%n}"));
31-
Assert.assertEquals(MessagePayloadUtils.prettyPrint("{\"text\":\"<?;,{}' '[]:>\"}"),
34+
assertEquals(MessagePayloadUtils.prettyPrint("{\"text\":\"<?;,{}' '[]:>\"}"),
3235
String.format("{%n \"text\": \"<?;,{}' '[]:>\"%n}"));
33-
Assert.assertEquals(MessagePayloadUtils.prettyPrint(String.format("%n%n { \"user\":%n%n \"citrus\" }")),
36+
assertEquals(MessagePayloadUtils.prettyPrint(String.format("%n%n { \"user\":%n%n \"citrus\" }")),
3437
String.format("{%n \"user\": \"citrus\"%n}"));
35-
Assert.assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"age\": 32}"),
38+
assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"age\": 32}"),
3639
String.format("{%n \"user\": \"citrus\",%n \"age\": 32%n}"));
37-
Assert.assertEquals(MessagePayloadUtils.prettyPrint("[22, 32]"),
40+
assertEquals(MessagePayloadUtils.prettyPrint("[22, 32]"),
3841
String.format("[%n22,%n32%n]"));
39-
Assert.assertEquals(MessagePayloadUtils.prettyPrint("[{\"user\":\"citrus\",\"age\": 32}]"),
42+
assertEquals(MessagePayloadUtils.prettyPrint("[{\"user\":\"citrus\",\"age\": 32}]"),
4043
String.format("[%n {%n \"user\": \"citrus\",%n \"age\": 32%n }%n]"));
41-
Assert.assertEquals(MessagePayloadUtils.prettyPrint("[{\"user\":\"citrus\",\"age\": 32}, {\"user\":\"foo\",\"age\": 99}]"),
44+
assertEquals(MessagePayloadUtils.prettyPrint("[{\"user\":\"citrus\",\"age\": 32}, {\"user\":\"foo\",\"age\": 99}]"),
4245
String.format("[%n {%n \"user\": \"citrus\",%n \"age\": 32%n },%n {%n \"user\": \"foo\",%n \"age\": 99%n }%n]"));
43-
Assert.assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"age\": 32,\"pet\":{\"name\": \"fluffy\", \"age\": 4}}"),
46+
assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"age\": 32,\"pet\":{\"name\": \"fluffy\", \"age\": 4}}"),
4447
String.format("{%n \"user\": \"citrus\",%n \"age\": 32,%n \"pet\": {%n \"name\": \"fluffy\",%n \"age\": 4%n }%n}"));
45-
Assert.assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"age\": 32,\"pets\":[\"fluffy\",\"hasso\"]}"),
48+
assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"age\": 32,\"pets\":[\"fluffy\",\"hasso\"]}"),
4649
String.format("{%n \"user\": \"citrus\",%n \"age\": 32,%n \"pets\": [%n \"fluffy\",%n \"hasso\"%n ]%n}"));
47-
Assert.assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"pets\":[\"fluffy\",\"hasso\"],\"age\": 32}"),
50+
assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"pets\":[\"fluffy\",\"hasso\"],\"age\": 32}"),
4851
String.format("{%n \"user\": \"citrus\",%n \"pets\": [%n \"fluffy\",%n \"hasso\"%n ],%n \"age\": 32%n}"));
49-
Assert.assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"pets\":[{\"name\": \"fluffy\", \"age\": 4},{\"name\": \"hasso\", \"age\": 2}],\"age\": 32}"),
52+
assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"pets\":[{\"name\": \"fluffy\", \"age\": 4},{\"name\": \"hasso\", \"age\": 2}],\"age\": 32}"),
5053
String.format("{%n \"user\": \"citrus\",%n \"pets\": [%n {%n \"name\": \"fluffy\",%n \"age\": 4%n },%n {%n \"name\": \"hasso\",%n \"age\": 2%n }%n ],%n \"age\": 32%n}"));
5154
}
5255

5356
@Test
5457
public void shouldPrettyPrintXml() {
55-
Assert.assertEquals(MessagePayloadUtils.prettyPrint(""), "");
56-
Assert.assertEquals(MessagePayloadUtils.prettyPrint("<root></root>"),
58+
assertEquals(MessagePayloadUtils.prettyPrint(""), "");
59+
assertEquals(MessagePayloadUtils.prettyPrint("<root></root>"),
5760
String.format("<root>%n</root>%n"));
58-
Assert.assertEquals(MessagePayloadUtils.prettyPrint("<root><text>Citrus rocks!</text></root>"),
61+
assertEquals(MessagePayloadUtils.prettyPrint("<root><text>Citrus rocks!</text></root>"),
5962
String.format("<root>%n <text>Citrus rocks!</text>%n</root>%n"));
60-
Assert.assertEquals(MessagePayloadUtils.prettyPrint("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root><text>Citrus rocks!</text></root>"),
63+
assertEquals(MessagePayloadUtils.prettyPrint("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root><text>Citrus rocks!</text></root>"),
6164
String.format("<?xml version=\"1.0\" encoding=\"UTF-8\"?>%n<root>%n <text>Citrus rocks!</text>%n</root>%n"));
62-
Assert.assertEquals(MessagePayloadUtils.prettyPrint(String.format("<root>%n<text>%nCitrus rocks!%n</text>%n</root>")),
65+
assertEquals(MessagePayloadUtils.prettyPrint(String.format("<root>%n<text>%nCitrus rocks!%n</text>%n</root>")),
6366
String.format("<root>%n <text>Citrus rocks!</text>%n</root>%n"));
64-
Assert.assertEquals(MessagePayloadUtils.prettyPrint(String.format("<root>%n <text language=\"eng\">%nCitrus rocks!%n </text>%n</root>")),
67+
assertEquals(MessagePayloadUtils.prettyPrint(String.format("<root>%n <text language=\"eng\">%nCitrus rocks!%n </text>%n</root>")),
6568
String.format("<root>%n <text language=\"eng\">Citrus rocks!</text>%n</root>%n"));
66-
Assert.assertEquals(MessagePayloadUtils.prettyPrint(String.format("%n%n <root><text language=\"eng\"><![CDATA[Citrus rocks!]]></text></root>")),
69+
assertEquals(MessagePayloadUtils.prettyPrint(String.format("%n%n <root><text language=\"eng\"><![CDATA[Citrus rocks!]]></text></root>")),
6770
String.format("<root>%n <text language=\"eng\">%n <![CDATA[Citrus rocks!]]>%n </text>%n</root>%n"));
68-
Assert.assertEquals(MessagePayloadUtils.prettyPrint(String.format("<root><text language=\"eng\" important=\"true\"><![CDATA[%n Citrus rocks!%n ]]></text></root>")),
71+
assertEquals(MessagePayloadUtils.prettyPrint(String.format("<root><text language=\"eng\" important=\"true\"><![CDATA[%n Citrus rocks!%n ]]></text></root>")),
6972
String.format("<root>%n <text language=\"eng\" important=\"true\">%n <![CDATA[%n Citrus rocks!%n ]]>%n </text>%n</root>%n"));
7073
}
74+
75+
@DataProvider
76+
public Object[][] normalizeWhitespaceProvider() {
77+
return new Object[][] {
78+
// Test data: payload, ignoreWhitespace, ignoreNewLineType, expected result
79+
{"Hello \t\r\nWorld\r\n", true, true, "Hello World"},
80+
{"Hello \t\r\nWorld\r\n", true, false, "Hello World"},
81+
{"Hello \t\r\nWorld\r\n", false, true, "Hello \t\nWorld\n"},
82+
{"Hello \t\r\nWorld\r\n", false, false, "Hello \t\r\nWorld\r\n"},
83+
{"", true, true, ""},
84+
{"", false, false, ""},
85+
{null, true, true, null},
86+
{null, false, false, null}
87+
};
88+
}
89+
90+
@Test(dataProvider = "normalizeWhitespaceProvider")
91+
public void normalizeWhitespace(String text, boolean normalizeWhitespace, boolean normalizeLineEndingsToUnix, String expected) {
92+
assertEquals(MessagePayloadUtils.normalizeWhitespace(text, normalizeWhitespace, normalizeLineEndingsToUnix), expected);
93+
}
7194
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.citrusframework.testapi;
2+
3+
import org.citrusframework.TestAction;
4+
import org.citrusframework.actions.SendMessageAction.SendMessageActionBuilder;
5+
import org.citrusframework.context.TestContext;
6+
7+
/**
8+
* Implementors of this interface are used to customize the SendMessageActionBuilder with application specific information. E.g. cookies
9+
* or transactionIds.
10+
*/
11+
public interface ApiActionBuilderCustomizerService {
12+
<T extends SendMessageActionBuilder<?,?,?>> T build(GeneratedApi generatedApi, TestAction action, TestContext context, T builder);
13+
}

Diff for: pom.xml

+20
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@
175175
<maven.nexus-staging.plugin.version>1.6.13</maven.nexus-staging.plugin.version>
176176
<maven.plugin.plugin.version>3.9.0</maven.plugin.plugin.version>
177177
<maven.plugin.annotations.version>3.13.1</maven.plugin.annotations.version>
178+
<maven.plugin.testing.harness.version>3.3.0</maven.plugin.testing.harness.version>
178179
<maven.release.plugin.version>3.0.1</maven.release.plugin.version>
179180
<maven.resource.plugin.version>3.3.1</maven.resource.plugin.version>
180181
<maven.scm.plugin.version>1.11.2</maven.scm.plugin.version>
@@ -249,6 +250,7 @@
249250
<mockftpserver.version>3.2.0</mockftpserver.version>
250251
<netty.version>4.1.105.Final</netty.version>
251252
<okhttp.version>4.12.0</okhttp.version>
253+
<openapi-generator-maven-plugin>7.5.0</openapi-generator-maven-plugin>
252254
<picoli-version>4.7.6</picoli-version>
253255
<saaj.version>3.0.4</saaj.version>
254256
<selenium.version>4.25.0</selenium.version>
@@ -519,6 +521,19 @@
519521
<artifactId>awaitility</artifactId>
520522
<version>${awaitility.version}</version>
521523
</dependency>
524+
525+
<dependency>
526+
<groupId>org.openapitools</groupId>
527+
<artifactId>openapi-generator</artifactId>
528+
<version>${openapi-generator-maven-plugin}</version>
529+
<scope>provided</scope>
530+
</dependency>
531+
532+
<dependency>
533+
<groupId>org.springframework</groupId>
534+
<artifactId>spring-test</artifactId>
535+
<version>${spring.version}</version>
536+
</dependency>
522537
<dependency>
523538
<groupId>org.testng</groupId>
524539
<artifactId>testng</artifactId>
@@ -890,6 +905,11 @@
890905
<artifactId>jackson-databind</artifactId>
891906
<version>${jackson.version}</version>
892907
</dependency>
908+
<dependency>
909+
<groupId>com.fasterxml.jackson.dataformat</groupId>
910+
<artifactId>jackson-dataformat-yaml</artifactId>
911+
<version>${jackson.version}</version>
912+
</dependency>
893913
<dependency>
894914
<groupId>com.fasterxml.jackson.module</groupId>
895915
<artifactId>jackson-module-jaxb-annotations</artifactId>

0 commit comments

Comments
 (0)