openApiRepository.openApi(openApiAlias)).filter(
+ Objects::nonNull).findFirst().orElseThrow(() ->
+ new CitrusRuntimeException(
+ "Unable to resolve OpenApiSpecification from alias '%s'. Known aliases for open api specs are '%s'".formatted(
+ openApiAlias, OpenApiUtils.getKnownOpenApiAliases(resolver)))
+ );
+ } else {
+ throw new CitrusRuntimeException(
+ "Unable to resolve OpenApiSpecification. Neither OpenAPI spec, nor OpenAPI alias are specified.");
+ }
+ }
+
+ if (httpClient != null) {
+ openApiSpecification.setHttpClient(httpClient);
+ }
+
+ return openApiSpecification;
+ }
+
+ public void setHttpClient(String httpClient) {
+ this.httpClient = httpClient;
+ }
+}
diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/model/OperationPathAdapter.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/model/OperationPathAdapter.java
index c1a1999ac9..e5aebaea7e 100644
--- a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/model/OperationPathAdapter.java
+++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/model/OperationPathAdapter.java
@@ -30,7 +30,7 @@
* @param fullPath The full path combining context path and API path.
* @param operation The {@link OasOperation} object representing the operation details.
*/
-public record OperationPathAdapter(String apiPath, String contextPath, String fullPath, OasOperation operation) {
+public record OperationPathAdapter(String apiPath, String contextPath, String fullPath, OasOperation operation, String uniqueOperationId) {
@Override
public String toString() {
diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/util/OpenApiUtils.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/util/OpenApiUtils.java
index ae4008111a..3e9ea0951e 100644
--- a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/util/OpenApiUtils.java
+++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/util/OpenApiUtils.java
@@ -21,9 +21,12 @@
import io.apicurio.datamodels.openapi.models.OasOperation;
import io.apicurio.datamodels.openapi.models.OasSchema;
import jakarta.annotation.Nonnull;
+import java.util.stream.Collectors;
import org.citrusframework.http.message.HttpMessage;
import org.citrusframework.http.message.HttpMessageHeaders;
import org.citrusframework.openapi.OpenApiConstants;
+import org.citrusframework.openapi.OpenApiRepository;
+import org.citrusframework.spi.ReferenceResolver;
import org.citrusframework.util.StringUtils;
public class OpenApiUtils {
@@ -73,4 +76,18 @@ public static boolean isRequired(OasSchema schema, String field) {
return schema.required.contains(field);
}
+ /**
+ * Retrieves all known OpenAPI aliases from {@link org.citrusframework.openapi.OpenApiSpecification}s
+ * registered in {@link OpenApiRepository}s.
+ *
+ * @param resolver the {@code ReferenceResolver} to use for resolving {@code OpenApiRepository} instances.
+ * @return a comma-separated string of all known OpenAPI aliases.
+ */
+ public static String getKnownOpenApiAliases(ReferenceResolver resolver) {
+ return resolver.resolveAll(OpenApiRepository.class).values()
+ .stream().flatMap(
+ openApiRepository -> openApiRepository.getOpenApiSpecifications()
+ .stream()).flatMap(spec -> spec.getAliases().stream()).collect(
+ Collectors.joining(", "));
+ }
}
diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiMessageProcessor.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiMessageProcessor.java
new file mode 100644
index 0000000000..d20c899fc2
--- /dev/null
+++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiMessageProcessor.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.citrusframework.openapi.validation;
+
+import org.citrusframework.openapi.OpenApiMessageType;
+import org.citrusframework.context.TestContext;
+import org.citrusframework.message.Message;
+import org.citrusframework.message.MessageProcessor;
+import org.citrusframework.openapi.OpenApiMessageHeaders;
+import org.citrusframework.openapi.OpenApiSpecification;
+
+/**
+ * {@code MessageProcessor} that prepares the message for OpenAPI validation by setting respective
+ * message headers.
+ */
+public class OpenApiMessageProcessor implements MessageProcessor {
+
+ private final OpenApiSpecification openApiSpecification;
+
+ private final String operationId;
+
+ private final OpenApiMessageType type;
+
+ public OpenApiMessageProcessor(OpenApiSpecification openApiSpecification,
+ String operationId, OpenApiMessageType type) {
+ this.operationId = operationId;
+ this.openApiSpecification = openApiSpecification;
+ this.type = type;
+ }
+
+ @Override
+ public void process(Message message, TestContext context) {
+
+ openApiSpecification
+ .getOperation(operationId, context)
+ .ifPresent(operationPathAdapter -> {
+ // Store the uniqueId of the operation, rather than the operationId, to avoid clashes.
+ message.setHeader(OpenApiMessageHeaders.OAS_UNIQUE_OPERATION_ID, operationPathAdapter.uniqueOperationId());
+ message.setHeader(OpenApiMessageHeaders.OAS_MESSAGE_TYPE, type.toHeaderName());
+ });
+ }
+}
\ No newline at end of file
diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiMessageValidationContext.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiMessageValidationContext.java
new file mode 100644
index 0000000000..cc79a4a5a4
--- /dev/null
+++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiMessageValidationContext.java
@@ -0,0 +1,117 @@
+package org.citrusframework.openapi.validation;
+
+import org.citrusframework.openapi.OpenApiSettings;
+import org.citrusframework.openapi.OpenApiSpecification;
+import org.citrusframework.validation.context.DefaultValidationContext;
+import org.citrusframework.validation.context.SchemaValidationContext;
+import org.citrusframework.validation.context.ValidationContext;
+
+/**
+ * Validation context holding OpenAPI specific validation information.
+ *
+ * @since 4.3
+ */
+public class OpenApiMessageValidationContext extends DefaultValidationContext implements
+ SchemaValidationContext {
+
+ /**
+ * Should message be validated with its schema definition. This is enabled with respect to
+ * global settings, which are true by default. as only messages processed by open api actions
+ * will be processed and validation information will be derived from open api spec.
+ *
+ * Note that the default registered validation context is used for received messages. This is
+ * why the schema validation is initialized with response validation enabled globally.
+ */
+ private boolean schemaValidation = OpenApiSettings.isResponseValidationEnabledGlobally();
+
+
+ public OpenApiMessageValidationContext(Builder builder) {
+ super();
+
+ // If not explicitly specified, goe for the default.
+ this.schemaValidation = builder.schemaValidation != null ? builder.schemaValidation
+ : builder.openApiSpecification.isApiRequestValidationEnabled()
+ || builder.openApiSpecification.isApiResponseValidationEnabled();
+
+ }
+
+ @Override
+ public boolean isSchemaValidationEnabled() {
+ return schemaValidation;
+ }
+
+ @Override
+ public String getSchemaRepository() {
+ return null;
+ }
+
+ @Override
+ public String getSchema() {
+ return null;
+ }
+
+ /**
+ * Fluent builder
+ */
+ public static final class Builder implements
+ ValidationContext.Builder,
+ SchemaValidationContext.Builder {
+
+ private OpenApiSpecification openApiSpecification;
+
+ /**
+ * Mapped as object to be able to indicate "not explicitly set" in which case the default is
+ * chosen.
+ *
+ * Note that a message validation context is explicitly created only for send messages,
+ * whereas default request validation enabled is chosen as default value.
+ */
+ private Boolean schemaValidation = OpenApiSettings.isRequestValidationEnabledGlobally();
+
+ public static OpenApiMessageValidationContext.Builder openApi(
+ OpenApiSpecification openApiSpecification) {
+ Builder builder = new Builder();
+ builder.openApiSpecification = openApiSpecification;
+ return builder;
+ }
+
+ public OpenApiMessageValidationContext.Builder expressions() {
+ return new OpenApiMessageValidationContext.Builder();
+ }
+
+ public OpenApiMessageValidationContext.Builder expression(String path,
+ Object expectedValue) {
+ return new OpenApiMessageValidationContext.Builder().expression(path, expectedValue);
+ }
+
+ /**
+ * Sets schema validation enabled/disabled for this message.
+ */
+ public OpenApiMessageValidationContext.Builder schemaValidation(final boolean enabled) {
+ this.schemaValidation = enabled;
+ return this;
+ }
+
+ /**
+ * Not used for open api validation. Schema is automatically be derived from associated openApiSpecification.
+ */
+ @Override
+ public OpenApiMessageValidationContext.Builder schema(final String schemaName) {
+ return this;
+ }
+
+ /**
+ * Not used for open api validation. Schema is automatically be derived from associated openApiSpecification.
+ */
+ @Override
+ public OpenApiMessageValidationContext.Builder schemaRepository(
+ final String schemaRepository) {
+ return this;
+ }
+
+ @Override
+ public OpenApiMessageValidationContext build() {
+ return new OpenApiMessageValidationContext(this);
+ }
+ }
+}
diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiRequestValidationProcessor.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiRequestValidationProcessor.java
index cb14d44c89..e8c49cb2b6 100644
--- a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiRequestValidationProcessor.java
+++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiRequestValidationProcessor.java
@@ -19,6 +19,7 @@
import org.citrusframework.context.TestContext;
import org.citrusframework.http.message.HttpMessage;
import org.citrusframework.message.Message;
+import org.citrusframework.openapi.OpenApiMessageHeaders;
import org.citrusframework.openapi.OpenApiSpecification;
import org.citrusframework.validation.ValidationProcessor;
@@ -49,6 +50,8 @@ public void validate(Message message, TestContext context) {
return;
}
+ message.setHeader(OpenApiMessageHeaders.OAS_UNIQUE_OPERATION_ID, operationId);
+
openApiSpecification.getOperation(
operationId, context).ifPresent(operationPathAdapter ->
openApiRequestValidator.validateRequest(operationPathAdapter, httpMessage));
diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiRequestValidator.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiRequestValidator.java
index 94aa08ae9a..b37e53f660 100644
--- a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiRequestValidator.java
+++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiRequestValidator.java
@@ -57,6 +57,17 @@ public void validateRequest(OperationPathAdapter operationPathAdapter,
}
}
+ public ValidationReport validateRequestToReport(OperationPathAdapter operationPathAdapter,
+ HttpMessage requestMessage) {
+
+ if (enabled && openApiInteractionValidator != null) {
+ return openApiInteractionValidator.validateRequest(
+ createRequestFromMessage(operationPathAdapter, requestMessage));
+ }
+
+ return ValidationReport.empty();
+ }
+
Request createRequestFromMessage(OperationPathAdapter operationPathAdapter,
HttpMessage httpMessage) {
var payload = httpMessage.getPayload();
diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiResponseValidationProcessor.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiResponseValidationProcessor.java
deleted file mode 100644
index c098fda6a0..0000000000
--- a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiResponseValidationProcessor.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.citrusframework.openapi.validation;
-
-import org.citrusframework.context.TestContext;
-import org.citrusframework.http.message.HttpMessage;
-import org.citrusframework.message.Message;
-import org.citrusframework.openapi.OpenApiSpecification;
-import org.citrusframework.validation.ValidationProcessor;
-
-/**
- * {@code ValidationProcessor} that delegates validation of OpenApi responses to instances of
- * {@link OpenApiResponseValidator}.
- */
-public class OpenApiResponseValidationProcessor implements
- ValidationProcessor {
-
- private final OpenApiSpecification openApiSpecification;
-
- private final String operationId;
-
- private final OpenApiResponseValidator openApiResponseValidator;
-
- public OpenApiResponseValidationProcessor(OpenApiSpecification openApiSpecification,
- String operationId) {
- this.operationId = operationId;
- this.openApiSpecification = openApiSpecification;
- this.openApiResponseValidator = new OpenApiResponseValidator(openApiSpecification);
- }
-
- @Override
- public void validate(Message message, TestContext context) {
-
- if (!(message instanceof HttpMessage httpMessage)) {
- return;
- }
-
- openApiSpecification.getOperation(
- operationId, context).ifPresent(operationPathAdapter ->
- openApiResponseValidator.validateResponse(operationPathAdapter, httpMessage));
- }
-
-}
diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiResponseValidator.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiResponseValidator.java
index faefe24a9b..8a08242617 100644
--- a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiResponseValidator.java
+++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiResponseValidator.java
@@ -59,7 +59,23 @@ public void validateResponse(OperationPathAdapter operationPathAdapter, HttpMess
}
}
- Response createResponseFromMessage(HttpMessage message, Integer statusCode) {
+ public ValidationReport validateResponseToReport(OperationPathAdapter operationPathAdapter, HttpMessage httpMessage) {
+
+ if (enabled && openApiInteractionValidator != null) {
+ HttpStatusCode statusCode = httpMessage.getStatusCode();
+ Response response = createResponseFromMessage(httpMessage,
+ statusCode != null ? statusCode.value() : null);
+
+ return openApiInteractionValidator.validateResponse(
+ operationPathAdapter.apiPath(),
+ Method.valueOf(operationPathAdapter.operation().getMethod().toUpperCase()),
+ response);
+
+ }
+ return ValidationReport.empty();
+ }
+
+ Response createResponseFromMessage(HttpMessage message, Integer statusCode) {
var payload = message.getPayload();
SimpleResponse.Builder responseBuilder = new SimpleResponse.Builder(statusCode);
diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiSchemaValidation.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiSchemaValidation.java
new file mode 100644
index 0000000000..74b5a55046
--- /dev/null
+++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/validation/OpenApiSchemaValidation.java
@@ -0,0 +1,166 @@
+package org.citrusframework.openapi.validation;
+
+import com.atlassian.oai.validator.report.ValidationReport;
+import jakarta.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+import org.citrusframework.context.TestContext;
+import org.citrusframework.exceptions.CitrusRuntimeException;
+import org.citrusframework.exceptions.ValidationException;
+import org.citrusframework.http.message.HttpMessage;
+import org.citrusframework.message.Message;
+import org.citrusframework.openapi.OpenApiMessageHeaders;
+import org.citrusframework.openapi.OpenApiRepository;
+import org.citrusframework.openapi.OpenApiSpecification;
+import org.citrusframework.openapi.model.OperationPathAdapter;
+import org.citrusframework.openapi.util.OpenApiUtils;
+import org.citrusframework.openapi.validation.OpenApiMessageValidationContext.Builder;
+import org.citrusframework.validation.MessageValidator;
+import org.citrusframework.validation.SchemaValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OpenApiSchemaValidation implements MessageValidator,
+ SchemaValidator {
+
+ private Logger logger = LoggerFactory.getLogger(OpenApiSchemaValidation.class);
+
+ @Override
+ public void validateMessage(Message receivedMessage, Message controlMessage,
+ TestContext context, List list) throws ValidationException {
+ validate(receivedMessage, context, new Builder().schemaValidation(true).build());
+ }
+
+ @Override
+ public void validate(
+ Message message, TestContext context, OpenApiMessageValidationContext validationContext) {
+ logger.debug("Starting OpenApi schema validation ...");
+
+ if (!(message instanceof HttpMessage httpMessage)) {
+ return;
+ }
+
+ ValidationReportData validationReportData = validate(context, httpMessage,
+ findSchemaRepositories(context),
+ validationContext);
+ if (validationReportData != null && validationReportData.report.hasErrors()) {
+ if (logger.isErrorEnabled()) {
+ logger.error("Failed to validate Json schema for message:\n{}",
+ message.getPayload(String.class));
+ }
+ throw new ValidationException(constructErrorMessage(validationReportData));
+ }
+
+ logger.debug("Json schema validation successful: All values OK");
+ }
+
+ @Override
+ public boolean supportsMessageType(String messageType, Message message) {
+ return message.getHeader(OpenApiMessageHeaders.OAS_UNIQUE_OPERATION_ID) != null;
+ }
+
+ private String constructErrorMessage(ValidationReportData validationReportData) {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("OpenApi ");
+ stringBuilder.append(validationReportData.type);
+ stringBuilder.append(" validation failed for operation: ");
+ stringBuilder.append(validationReportData.operationPathAdapter);
+ validationReportData.report.getMessages()
+ .forEach(message -> stringBuilder.append("\n\t").append(message));
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Find json schema repositories in test context.
+ */
+ private List findSchemaRepositories(TestContext context) {
+ return new ArrayList<>(
+ context.getReferenceResolver().resolveAll(OpenApiRepository.class).values());
+ }
+
+ @Nullable
+ private ValidationReportData validate(TestContext context, HttpMessage message,
+ List schemaRepositories,
+ OpenApiMessageValidationContext validationContext) {
+
+ if (!validationContext.isSchemaValidationEnabled()) {
+ return null;
+ }
+ if (schemaRepositories.isEmpty()) {
+ return null;
+ } else {
+
+ // Is it request or response?
+ String uniqueOperationId = (String) message.getHeader(
+ OpenApiMessageHeaders.OAS_UNIQUE_OPERATION_ID);
+
+ OpenApiSpecification openApiSpecification = schemaRepositories
+ .stream()
+ .flatMap(repository -> repository.getOpenApiSpecifications().stream())
+ .filter(spec -> spec.getOperation(uniqueOperationId,
+ context).isPresent()).findFirst().orElse(null);
+
+ if (openApiSpecification == null) {
+ throw new CitrusRuntimeException("""
+ Unable to derive OpenAPI spec for operation '%s' for validation of message from available "
+ schema repositories. Known repository aliases are: %s""".formatted(
+ uniqueOperationId, OpenApiUtils.getKnownOpenApiAliases(
+ context.getReferenceResolver())));
+ }
+
+ OperationPathAdapter operationPathAdapter = openApiSpecification.getOperation(
+ uniqueOperationId, context).orElseThrow(() -> new CitrusRuntimeException(
+ "Unexpectedly could not resolve operation path adapter for operationId: "
+ + uniqueOperationId));
+ ValidationReportData validationReportData = null;
+ if (isRequestMessage(message)) {
+ ValidationReport validationReport = new OpenApiRequestValidator(
+ openApiSpecification).validateRequestToReport(operationPathAdapter, message);
+ validationReportData = new ValidationReportData(operationPathAdapter, "request",
+ validationReport);
+ } else if (isResponseMessage(message)) {
+ ValidationReport validationReport = new OpenApiResponseValidator(
+ openApiSpecification).validateResponseToReport(operationPathAdapter, message);
+ validationReportData = new ValidationReportData(operationPathAdapter, "response",
+ validationReport);
+ }
+ return validationReportData;
+ }
+ }
+
+ private boolean isResponseMessage(HttpMessage message) {
+ return OpenApiMessageHeaders.RESPONSE_TYPE.equals(
+ message.getHeader(OpenApiMessageHeaders.OAS_MESSAGE_TYPE));
+ }
+
+ private boolean isRequestMessage(HttpMessage message) {
+ return OpenApiMessageHeaders.REQUEST_TYPE.equals(
+ message.getHeader(OpenApiMessageHeaders.OAS_MESSAGE_TYPE));
+ }
+
+ private record ValidationReportData(OperationPathAdapter operationPathAdapter, String type,
+ ValidationReport report) {
+
+ }
+
+ @Override
+ public boolean canValidate(Message message, boolean schemaValidationEnabled) {
+ return schemaValidationEnabled &&
+ message instanceof HttpMessage httpMessage && (isRequestMessage(httpMessage)
+ || isResponseMessage(httpMessage));
+ }
+
+ @Override
+ public void validate(Message message, TestContext context, String schemaRepository,
+ String schema) {
+
+ if (!(message instanceof HttpMessage)) {
+ return;
+ }
+
+ validate(message, context,
+ new Builder().schemaValidation(true).schema(schema).schemaRepository(schemaRepository)
+ .build());
+
+ }
+}
diff --git a/connectors/citrus-openapi/src/main/resources/META-INF/citrus/message/schemaValidator/openApi b/connectors/citrus-openapi/src/main/resources/META-INF/citrus/message/schemaValidator/openApi
new file mode 100644
index 0000000000..897edb75c0
--- /dev/null
+++ b/connectors/citrus-openapi/src/main/resources/META-INF/citrus/message/schemaValidator/openApi
@@ -0,0 +1,2 @@
+name=defaultOpenApiSchemaValidator
+type=org.citrusframework.openapi.validation.OpenApiSchemaValidation
diff --git a/connectors/citrus-openapi/src/main/resources/META-INF/citrus/message/validator/openApi b/connectors/citrus-openapi/src/main/resources/META-INF/citrus/message/validator/openApi
new file mode 100644
index 0000000000..ad5d27c497
--- /dev/null
+++ b/connectors/citrus-openapi/src/main/resources/META-INF/citrus/message/validator/openApi
@@ -0,0 +1,2 @@
+name=defaultOpenApiMessageValidator
+type=org.citrusframework.openapi.validation.OpenApiSchemaValidation
diff --git a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/OpenApiMessageTypeTest.java b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/OpenApiMessageTypeTest.java
new file mode 100644
index 0000000000..39486d14f8
--- /dev/null
+++ b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/OpenApiMessageTypeTest.java
@@ -0,0 +1,18 @@
+package org.citrusframework.openapi;
+
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.Test;
+
+public class OpenApiMessageTypeTest {
+
+ @Test
+ public void testToHeaderNameRequest() {
+ assertEquals(OpenApiMessageType.REQUEST.toHeaderName(), OpenApiMessageHeaders.REQUEST_TYPE);
+ }
+
+ @Test
+ public void testToHeaderNameResponse() {
+ assertEquals(OpenApiMessageHeaders.REQUEST_TYPE, OpenApiMessageHeaders.RESPONSE_TYPE);
+ }
+}
diff --git a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/OpenApiRepositoryTest.java b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/OpenApiRepositoryTest.java
index fe61791bf2..15f6fa113e 100644
--- a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/OpenApiRepositoryTest.java
+++ b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/OpenApiRepositoryTest.java
@@ -71,7 +71,7 @@ public void shouldResolveResourceAliasFromFile() {
Optional alias = OpenApiRepository.determineResourceAlias(resourceMock);
assertTrue(alias.isPresent());
- assertEquals(alias.get(), "MyApi.json");
+ assertEquals(alias.get(), "MyApi");
}
@Test
@@ -84,7 +84,7 @@ public void shouldResolveResourceAliasFromUrl() throws MalformedURLException {
Optional alias = OpenApiRepository.determineResourceAlias(resourceMock);
assertTrue(alias.isPresent());
- assertEquals(alias.get(), "MyApi.json");
+ assertEquals(alias.get(), "MyApi");
}
@Test
diff --git a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/OpenApiUtilsTest.java b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/OpenApiUtilsTest.java
index 9dbc709fa6..533ccb5211 100644
--- a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/OpenApiUtilsTest.java
+++ b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/OpenApiUtilsTest.java
@@ -16,17 +16,25 @@
package org.citrusframework.openapi;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import org.citrusframework.http.message.HttpMessage;
import org.citrusframework.http.message.HttpMessageHeaders;
import org.citrusframework.openapi.util.OpenApiUtils;
+import org.citrusframework.openapi.validation.OpenApiMessageProcessor;
+import org.citrusframework.spi.ReferenceResolver;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import static org.citrusframework.openapi.util.OpenApiUtils.getKnownOpenApiAliases;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
public class OpenApiUtilsTest {
@@ -94,4 +102,56 @@ public void shouldReturnFormattedMethodPathWhenMethodAndPathAreEmpty() {
// Then
assertEquals(methodPath, "//");
}
+
+ @Test
+ public void testGetKnownOpenApiAliases() {
+
+ ReferenceResolver resolver = mock();
+ OpenApiRepository repository1 = mock();
+ OpenApiRepository repository2 = mock();
+ OpenApiSpecification spec1 = mock();
+ OpenApiSpecification spec2 = mock();
+
+ when(resolver.resolveAll(OpenApiRepository.class)).thenReturn(
+ Map.of(
+ "repo1", repository1,
+ "repo2", repository2
+ )
+ );
+
+ when(repository1.getOpenApiSpecifications()).thenReturn(List.of(spec1));
+ when(repository2.getOpenApiSpecifications()).thenReturn(List.of(spec2));
+
+ when(spec1.getAliases()).thenReturn(Set.of("alias1", "alias2"));
+ when(spec2.getAliases()).thenReturn(Set.of("alias3"));
+
+ String result = getKnownOpenApiAliases(resolver);
+
+ assertTrue(result.contains("alias1"));
+ assertTrue(result.contains("alias2"));
+ assertTrue(result.contains("alias3"));
+ }
+
+ @Test
+ public void testGetKnownOpenApiAliasesNoAliases() {
+ ReferenceResolver resolver = mock();
+ OpenApiRepository repository1 = mock();
+ OpenApiRepository repository2 = mock();
+
+ when(resolver.resolveAll(OpenApiRepository.class)).thenReturn(
+ Map.of(
+ "repo1", repository1,
+ "repo2", repository2
+ )
+ );
+
+ when(repository1.getOpenApiSpecifications()).thenReturn(List.of());
+ when(repository2.getOpenApiSpecifications()).thenReturn(List.of());
+
+ // Call the method under test
+ String result = getKnownOpenApiAliases(resolver);
+
+ // Verify the result
+ assertEquals(result, "");
+ }
}
diff --git a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/groovy/OpenApiClientTest.java b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/groovy/OpenApiClientTest.java
index 607d1b3c54..33aad7e924 100644
--- a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/groovy/OpenApiClientTest.java
+++ b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/groovy/OpenApiClientTest.java
@@ -178,7 +178,7 @@ public void shouldLoadOpenApiClientActions() throws IOException {
validator.validateMessage(request, controlMessage, context, new DefaultValidationContext());
ReceiveMessageAction receiveMessageAction = (ReceiveMessageAction) result.getTestAction(actionIndex++);
- Assert.assertEquals(receiveMessageAction.getValidationContexts().size(), 3);
+ Assert.assertEquals(receiveMessageAction.getValidationContexts().size(), 4);
Assert.assertEquals(receiveMessageAction.getReceiveTimeout(), 0L);
Assert.assertTrue(receiveMessageAction.getValidationContexts().get(0) instanceof HeaderValidationContext);
Assert.assertTrue(receiveMessageAction.getValidationContexts().get(1) instanceof XmlMessageValidationContext);
@@ -197,7 +197,7 @@ public void shouldLoadOpenApiClientActions() throws IOException {
Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_CONTENT_TYPE), "application/json");
Assert.assertNull(receiveMessageAction.getEndpoint());
Assert.assertEquals(receiveMessageAction.getEndpointUri(), "httpClient");
- Assert.assertEquals(receiveMessageAction.getMessageProcessors().size(), 0);
+ Assert.assertEquals(receiveMessageAction.getMessageProcessors().size(), 1);
Assert.assertEquals(receiveMessageAction.getControlMessageProcessors().size(), 0);
sendMessageAction = (SendMessageAction) result.getTestAction(actionIndex++);
@@ -218,7 +218,7 @@ public void shouldLoadOpenApiClientActions() throws IOException {
Assert.assertEquals(sendMessageAction.getEndpoint(), httpClient);
receiveMessageAction = (ReceiveMessageAction) result.getTestAction(actionIndex);
- Assert.assertEquals(receiveMessageAction.getValidationContexts().size(), 3);
+ Assert.assertEquals(receiveMessageAction.getValidationContexts().size(), 4);
Assert.assertTrue(receiveMessageAction.getValidationContexts().get(0) instanceof HeaderValidationContext);
Assert.assertTrue(receiveMessageAction.getValidationContexts().get(1) instanceof XmlMessageValidationContext);
Assert.assertTrue(receiveMessageAction.getValidationContexts().get(2) instanceof JsonMessageValidationContext);
diff --git a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/integration/OpenApiClientIT.java b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/integration/OpenApiClientIT.java
index 94786d08c2..c210208820 100644
--- a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/integration/OpenApiClientIT.java
+++ b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/integration/OpenApiClientIT.java
@@ -23,6 +23,7 @@
import org.citrusframework.http.client.HttpClientBuilder;
import org.citrusframework.http.server.HttpServer;
import org.citrusframework.http.server.HttpServerBuilder;
+import org.citrusframework.openapi.OpenApiRepository;
import org.citrusframework.openapi.OpenApiSpecification;
import org.citrusframework.openapi.actions.OpenApiActionBuilder;
import org.citrusframework.openapi.actions.OpenApiClientResponseActionBuilder;
@@ -35,6 +36,8 @@
import org.testng.annotations.Ignore;
import org.testng.annotations.Test;
+import java.util.List;
+
import static org.citrusframework.http.actions.HttpActionBuilder.http;
import static org.citrusframework.openapi.actions.OpenApiActionBuilder.openapi;
import static org.testng.Assert.assertThrows;
@@ -61,8 +64,12 @@ public class OpenApiClientIT extends TestNGCitrusSpringSupport {
.requestUrl("http://localhost:%d".formatted(port))
.build();
+ @BindToRegistry
+ private final OpenApiRepository openApiRepository = new OpenApiRepository()
+ .locations(List.of("classpath:org/citrusframework/openapi/petstore/petstore-v3.json"));
+
private final OpenApiSpecification petstoreSpec = OpenApiSpecification.from(
- Resources.create("classpath:org/citrusframework/openapi/petstore/petstore-v3.json"));
+ Resources.create("classpath:org/citrusframework/openapi/petstore/petstore-v3.json"));
private final OpenApiSpecification pingSpec = OpenApiSpecification.from(
Resources.create("classpath:org/citrusframework/openapi/ping/ping-api.yaml"));
@@ -140,8 +147,7 @@ public void shouldFailOnMissingNameInRequest() {
HttpMessageBuilderSupport addPetBuilder = openapi(petstoreSpec)
.client(httpClient)
.send("addPet")
- .message().body(Resources.create(INVALID_PET_PATH))
- .fork(true);
+ .message().body(Resources.create(INVALID_PET_PATH));
assertThrows(TestCaseFailedException.class, () ->when(addPetBuilder));
}
@@ -153,9 +159,7 @@ public void shouldFailOnWrongQueryIdType() {
HttpMessageBuilderSupport addPetBuilder = openapi(petstoreSpec)
.client(httpClient)
.send("addPet")
- .message().body(Resources.create(VALID_PET_PATH))
- .fork(true);
-
+ .message().body(Resources.create(VALID_PET_PATH));
assertThrows(TestCaseFailedException.class, () ->when(addPetBuilder));
}
@@ -167,8 +171,7 @@ public void shouldSucceedOnWrongQueryIdTypeWithOasDisabled() {
.client(httpClient)
.send("addPet")
.disableOasValidation(true)
- .message().body(Resources.create(VALID_PET_PATH))
- .fork(true);
+ .message().body(Resources.create(VALID_PET_PATH));
try {
when(addPetBuilder);
diff --git a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/integration/OpenApiServerIT.java b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/integration/OpenApiServerIT.java
index 82d48fa427..808c84cdc2 100644
--- a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/integration/OpenApiServerIT.java
+++ b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/integration/OpenApiServerIT.java
@@ -23,7 +23,7 @@
import org.citrusframework.http.client.HttpClientBuilder;
import org.citrusframework.http.server.HttpServer;
import org.citrusframework.http.server.HttpServerBuilder;
-import org.citrusframework.openapi.OpenApiSpecification;
+import org.citrusframework.openapi.OpenApiRepository;
import org.citrusframework.openapi.actions.OpenApiActionBuilder;
import org.citrusframework.openapi.actions.OpenApiServerRequestActionBuilder;
import org.citrusframework.openapi.actions.OpenApiServerResponseActionBuilder;
@@ -34,6 +34,8 @@
import org.springframework.http.HttpStatus;
import org.testng.annotations.Test;
+import java.util.List;
+
import static org.citrusframework.http.actions.HttpActionBuilder.http;
import static org.citrusframework.openapi.actions.OpenApiActionBuilder.openapi;
import static org.testng.Assert.assertThrows;
@@ -60,11 +62,10 @@ public class OpenApiServerIT extends TestNGCitrusSpringSupport {
.requestUrl("http://localhost:%d/petstore/v3".formatted(port))
.build();
- /**
- * Directly loaded open api.
- */
- private final OpenApiSpecification petstoreSpec = OpenApiSpecification.from(
- Resources.create("classpath:org/citrusframework/openapi/petstore/petstore-v3.json"));
+ @BindToRegistry
+ private final OpenApiRepository openApiRepository = new OpenApiRepository()
+ .locations(List.of("classpath:org/citrusframework/openapi/petstore/petstore-v3.json"));
+
@CitrusTest
public void shouldExecuteGetPetById() {
@@ -78,11 +79,11 @@ public void shouldExecuteGetPetById() {
.accept("application/json")
.fork(true));
- then(openapi(petstoreSpec)
+ then(openapi("petstore-v3")
.server(httpServer)
.receive("getPetById"));
- then(openapi(petstoreSpec)
+ then(openapi("petstore-v3")
.server(httpServer)
.send("getPetById", HttpStatus.OK));
@@ -118,11 +119,11 @@ public void executeGetPetByIdShouldFailOnInvalidResponse() {
.accept("application/json")
.fork(true));
- then(openapi(petstoreSpec)
+ then(openapi("petstore-v3")
.server(httpServer)
.receive("getPetById"));
- HttpMessageBuilderSupport getPetByIdResponseBuilder = openapi(petstoreSpec)
+ HttpMessageBuilderSupport getPetByIdResponseBuilder = openapi("petstore-v3")
.server(httpServer)
.send("getPetById", HttpStatus.OK)
.message().body("""
@@ -153,11 +154,11 @@ public void executeGetPetByIdShouldSucceedOnInvalidResponseWithValidationDisable
.accept("application/json")
.fork(true));
- then(openapi(petstoreSpec)
+ then(openapi("petstore-v3")
.server(httpServer)
.receive("getPetById"));
- HttpMessageBuilderSupport getPetByIdResponseBuilder = openapi(petstoreSpec)
+ HttpMessageBuilderSupport getPetByIdResponseBuilder = openapi("petstore-v3")
.server(httpServer)
.send("getPetById", HttpStatus.OK)
.disableOasValidation(true)
@@ -198,12 +199,12 @@ public void executeGetPetByIdShouldSucceedOnInvalidResponseWithValidationDisable
@CitrusTest
public void shouldExecuteAddPet() {
- shouldExecuteAddPet(openapi(petstoreSpec), VALID_PET_PATH, true);
+ shouldExecuteAddPet(openapi("petstore-v3"), VALID_PET_PATH, true);
}
@CitrusTest
public void shouldFailOnMissingNameInRequest() {
- shouldExecuteAddPet(openapi(petstoreSpec), INVALID_PET_PATH, false);
+ shouldExecuteAddPet(openapi("petstore-v3"), INVALID_PET_PATH, false);
}
@CitrusTest
@@ -218,11 +219,11 @@ public void shouldFailOnMissingNameInResponse() {
.accept("application/json")
.fork(true));
- then(openapi(petstoreSpec)
+ then(openapi("petstore-v3")
.server(httpServer)
.receive("getPetById"));
- OpenApiServerResponseActionBuilder sendMessageActionBuilder = openapi(petstoreSpec)
+ OpenApiServerResponseActionBuilder sendMessageActionBuilder = openapi("petstore-v3")
.server(httpServer)
.send("getPetById", HttpStatus.OK);
sendMessageActionBuilder.message().body(Resources.create(INVALID_PET_PATH));
@@ -244,7 +245,7 @@ public void shouldFailOnWrongQueryIdTypeWithOasDisabled() {
.contentType("application/json")
.fork(true));
- OpenApiServerRequestActionBuilder addPetBuilder = openapi(petstoreSpec)
+ OpenApiServerRequestActionBuilder addPetBuilder = openapi("petstore-v3")
.server(httpServer)
.receive("addPet");
@@ -264,7 +265,7 @@ public void shouldSucceedOnWrongQueryIdTypeWithOasDisabled() {
.contentType("application/json")
.fork(true));
- OpenApiServerRequestActionBuilder addPetBuilder = openapi(petstoreSpec)
+ OpenApiServerRequestActionBuilder addPetBuilder = openapi("petstore-v3")
.server(httpServer)
.receive("addPet")
.disableOasValidation(false);
@@ -293,18 +294,21 @@ private void shouldExecuteAddPet(OpenApiActionBuilder openapi, String requestFil
.receive("addPet");
if (valid) {
then(receiveActionBuilder);
- } else {
- assertThrows(() -> then(receiveActionBuilder));
- }
- then(openapi
+ then(openapi
.server(httpServer)
.send("addPet", HttpStatus.CREATED));
- then(http()
+ then(http()
.client(httpClient)
.receive()
.response(HttpStatus.CREATED));
+
+ } else {
+ assertThrows(() -> then(receiveActionBuilder));
+ }
+
+
}
}
diff --git a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/model/OperationPathAdapterTest.java b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/model/OperationPathAdapterTest.java
index d24101fb35..68c5da1786 100644
--- a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/model/OperationPathAdapterTest.java
+++ b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/model/OperationPathAdapterTest.java
@@ -31,7 +31,8 @@ public void shouldReturnFormattedStringWhenToStringIsCalled() {
Oas30Operation oas30Operation = new Oas30Operation("get");
oas30Operation.operationId = "operationId";
- OperationPathAdapter adapter = new OperationPathAdapter("/api/path", "/context/path", "/full/path", oas30Operation);
+ OperationPathAdapter adapter = new OperationPathAdapter("/api/path", "/context/path", "/full/path", oas30Operation,
+ oas30Operation.operationId);
// When
String expectedString = format("%s (%s)", OpenApiUtils.getMethodPath("GET", "/api/path"), "operationId");
diff --git a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/validation/OpenApiMessageProcessorTest.java b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/validation/OpenApiMessageProcessorTest.java
new file mode 100644
index 0000000000..34f82542a2
--- /dev/null
+++ b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/validation/OpenApiMessageProcessorTest.java
@@ -0,0 +1,59 @@
+package org.citrusframework.openapi.validation;
+
+import java.util.Optional;
+import org.citrusframework.context.TestContext;
+import org.citrusframework.message.Message;
+import org.citrusframework.openapi.OpenApiMessageHeaders;
+import org.citrusframework.openapi.OpenApiMessageType;
+import org.citrusframework.openapi.OpenApiSpecification;
+import org.citrusframework.openapi.model.OperationPathAdapter;
+import org.mockito.Mockito;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import static org.mockito.Mockito.*;
+import static org.testng.Assert.*;
+
+public class OpenApiMessageProcessorTest {
+
+ private OpenApiSpecification openApiSpecification;
+ private String operationId;
+ private OpenApiMessageType type;
+ private OpenApiMessageProcessor processor;
+ private Message message;
+ private TestContext context;
+
+ @BeforeMethod
+ public void setUp() {
+ openApiSpecification = mock(OpenApiSpecification.class);
+ operationId = "testOperationId";
+ type = mock(OpenApiMessageType.class);
+ processor = new OpenApiMessageProcessor(openApiSpecification, operationId, type);
+
+ message = mock(Message.class);
+ context = mock(TestContext.class);
+ }
+
+ @Test
+ public void testProcess() {
+ OperationPathAdapter operationPathAdapter = mock(OperationPathAdapter.class);
+ when(openApiSpecification.getOperation(operationId, context))
+ .thenReturn(Optional.of(operationPathAdapter));
+ when(operationPathAdapter.uniqueOperationId()).thenReturn("uniqueOperationId");
+ when(type.toHeaderName()).thenReturn("headerName");
+
+ processor.process(message, context);
+
+ verify(message).setHeader(OpenApiMessageHeaders.OAS_UNIQUE_OPERATION_ID, "uniqueOperationId");
+ verify(message).setHeader(OpenApiMessageHeaders.OAS_MESSAGE_TYPE, "headerName");
+ }
+
+ @Test
+ public void testProcessOperationNotPresent() {
+ when(openApiSpecification.getOperation(operationId, context))
+ .thenReturn(Optional.empty());
+
+ processor.process(message, context);
+
+ verify(message, never()).setHeader(anyString(), anyString());
+ }
+}
diff --git a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/validation/OpenApiResponseValidationProcessorTest.java b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/validation/OpenApiResponseValidationProcessorTest.java
deleted file mode 100644
index 2058af2558..0000000000
--- a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/validation/OpenApiResponseValidationProcessorTest.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.citrusframework.openapi.validation;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertNotNull;
-
-import java.util.Optional;
-import org.citrusframework.context.TestContext;
-import org.citrusframework.http.message.HttpMessage;
-import org.citrusframework.message.Message;
-import org.citrusframework.openapi.OpenApiSpecification;
-import org.citrusframework.openapi.model.OperationPathAdapter;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.springframework.test.util.ReflectionTestUtils;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-public class OpenApiResponseValidationProcessorTest {
-
- @Mock
- private OpenApiSpecification openApiSpecificationMock;
-
- @Mock
- private OperationPathAdapter operationPathAdapterMock;
-
- private OpenApiResponseValidationProcessor processor;
-
- private AutoCloseable mockCloseable;
-
- @BeforeMethod
- public void beforeMethod() {
- mockCloseable = MockitoAnnotations.openMocks(this);
- processor = new OpenApiResponseValidationProcessor(openApiSpecificationMock, "operationId");
- }
-
- @AfterMethod
- public void afterMethod() throws Exception {
- mockCloseable.close();
- }
-
- @Test
- public void shouldNotValidateNonHttpMessage() {
- Message messageMock = mock();
-
- processor.validate(messageMock, mock());
-
- verify(openApiSpecificationMock,times(2)).getSwaggerOpenApiValidationContext();
- verifyNoMoreInteractions(openApiSpecificationMock);
- }
-
- @Test
- public void shouldCallValidateResponse() {
- HttpMessage httpMessageMock = mock();
- TestContext contextMock = mock();
-
- OpenApiResponseValidator openApiResponseValidatorSpy = replaceValidatorWithSpy(httpMessageMock);
-
- when(openApiSpecificationMock.getOperation(anyString(), any(TestContext.class)))
- .thenReturn(Optional.of(operationPathAdapterMock));
-
- processor.validate(httpMessageMock, contextMock);
-
- verify(openApiResponseValidatorSpy).validateResponse(operationPathAdapterMock, httpMessageMock);
- }
-
- @Test
- public void shouldNotValidateWhenNoOperation() {
- HttpMessage httpMessageMock = mock();
- TestContext contextMock = mock();
-
- OpenApiResponseValidator openApiResponseValidatorSpy = replaceValidatorWithSpy(httpMessageMock);
-
- when(openApiSpecificationMock.getOperation(anyString(), any(TestContext.class)))
- .thenReturn(Optional.empty());
-
- processor.validate(httpMessageMock, contextMock);
-
- verify(openApiSpecificationMock).getOperation(anyString(),
- any(TestContext.class));
- verify(openApiResponseValidatorSpy, times(0)).validateResponse(operationPathAdapterMock, httpMessageMock);
- }
-
- private OpenApiResponseValidator replaceValidatorWithSpy(HttpMessage httpMessage) {
- OpenApiResponseValidator openApiResponseValidator = (OpenApiResponseValidator) ReflectionTestUtils.getField(
- processor,
- "openApiResponseValidator");
-
- assertNotNull(openApiResponseValidator);
- OpenApiResponseValidator openApiResponseValidatorSpy = spy(openApiResponseValidator);
- ReflectionTestUtils.setField(processor, "openApiResponseValidator", openApiResponseValidatorSpy);
-
- doAnswer((invocation) -> null
- // do nothing
- ).when(openApiResponseValidatorSpy).validateResponse(operationPathAdapterMock, httpMessage);
-
- return openApiResponseValidatorSpy;
- }
-}
diff --git a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/xml/OpenApiClientTest.java b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/xml/OpenApiClientTest.java
index ca2d7e8732..2fc0bb33bf 100644
--- a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/xml/OpenApiClientTest.java
+++ b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/xml/OpenApiClientTest.java
@@ -184,7 +184,7 @@ public void shouldLoadOpenApiClientActions() throws IOException {
validator.validateMessage(request, controlMessage, context, new DefaultValidationContext());
ReceiveMessageAction receiveMessageAction = (ReceiveMessageAction) result.getTestAction(actionIndex++);
- Assert.assertEquals(receiveMessageAction.getValidationContexts().size(), 3);
+ Assert.assertEquals(receiveMessageAction.getValidationContexts().size(), 4);
Assert.assertEquals(receiveMessageAction.getReceiveTimeout(), 0L);
Assert.assertTrue(receiveMessageAction.getValidationContexts().get(0) instanceof XmlMessageValidationContext);
Assert.assertTrue(receiveMessageAction.getValidationContexts().get(1) instanceof JsonMessageValidationContext);
@@ -203,7 +203,7 @@ public void shouldLoadOpenApiClientActions() throws IOException {
Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_CONTENT_TYPE), "application/json");
Assert.assertNull(receiveMessageAction.getEndpoint());
Assert.assertEquals(receiveMessageAction.getEndpointUri(), "httpClient");
- Assert.assertEquals(receiveMessageAction.getMessageProcessors().size(), 0);
+ Assert.assertEquals(receiveMessageAction.getMessageProcessors().size(), 1);
Assert.assertEquals(receiveMessageAction.getControlMessageProcessors().size(), 0);
sendMessageAction = (SendMessageAction) result.getTestAction(actionIndex++);
@@ -224,7 +224,7 @@ public void shouldLoadOpenApiClientActions() throws IOException {
Assert.assertEquals(sendMessageAction.getEndpointUri(), "httpClient");
receiveMessageAction = (ReceiveMessageAction) result.getTestAction(actionIndex);
- Assert.assertEquals(receiveMessageAction.getValidationContexts().size(), 3);
+ Assert.assertEquals(receiveMessageAction.getValidationContexts().size(), 4);
Assert.assertTrue(receiveMessageAction.getValidationContexts().get(0) instanceof XmlMessageValidationContext);
Assert.assertTrue(receiveMessageAction.getValidationContexts().get(1) instanceof JsonMessageValidationContext);
Assert.assertTrue(receiveMessageAction.getValidationContexts().get(2) instanceof HeaderValidationContext);
diff --git a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/yaml/OpenApiClientTest.java b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/yaml/OpenApiClientTest.java
index b5b152d559..e81a89e63b 100644
--- a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/yaml/OpenApiClientTest.java
+++ b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/yaml/OpenApiClientTest.java
@@ -179,7 +179,7 @@ public void shouldLoadOpenApiClientActions() throws IOException {
validator.validateMessage(request, controlMessage, context, new DefaultValidationContext());
ReceiveMessageAction receiveMessageAction = (ReceiveMessageAction) result.getTestAction(actionIndex++);
- Assert.assertEquals(receiveMessageAction.getValidationContexts().size(), 3);
+ Assert.assertEquals(receiveMessageAction.getValidationContexts().size(), 4);
Assert.assertEquals(receiveMessageAction.getReceiveTimeout(), 0L);
Assert.assertTrue(receiveMessageAction.getValidationContexts().get(0) instanceof XmlMessageValidationContext);
Assert.assertTrue(receiveMessageAction.getValidationContexts().get(1) instanceof JsonMessageValidationContext);
@@ -198,7 +198,7 @@ public void shouldLoadOpenApiClientActions() throws IOException {
Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_CONTENT_TYPE), "application/json");
Assert.assertNull(receiveMessageAction.getEndpoint());
Assert.assertEquals(receiveMessageAction.getEndpointUri(), "httpClient");
- Assert.assertEquals(receiveMessageAction.getMessageProcessors().size(), 0);
+ Assert.assertEquals(receiveMessageAction.getMessageProcessors().size(), 1);
Assert.assertEquals(receiveMessageAction.getControlMessageProcessors().size(), 0);
sendMessageAction = (SendMessageAction) result.getTestAction(actionIndex++);
@@ -219,7 +219,7 @@ public void shouldLoadOpenApiClientActions() throws IOException {
Assert.assertEquals(sendMessageAction.getEndpointUri(), "httpClient");
receiveMessageAction = (ReceiveMessageAction) result.getTestAction(actionIndex);
- Assert.assertEquals(receiveMessageAction.getValidationContexts().size(), 3);
+ Assert.assertEquals(receiveMessageAction.getValidationContexts().size(), 4);
Assert.assertTrue(receiveMessageAction.getValidationContexts().get(0) instanceof XmlMessageValidationContext);
Assert.assertTrue(receiveMessageAction.getValidationContexts().get(1) instanceof JsonMessageValidationContext);
Assert.assertTrue(receiveMessageAction.getValidationContexts().get(2) instanceof HeaderValidationContext);
diff --git a/core/citrus-api/src/main/java/org/citrusframework/validation/MessageValidatorRegistry.java b/core/citrus-api/src/main/java/org/citrusframework/validation/MessageValidatorRegistry.java
index 7ba3d3abd4..bbeecbcab6 100644
--- a/core/citrus-api/src/main/java/org/citrusframework/validation/MessageValidatorRegistry.java
+++ b/core/citrus-api/src/main/java/org/citrusframework/validation/MessageValidatorRegistry.java
@@ -112,7 +112,11 @@ public List> findMessageValidators
if (isEmptyOrDefault(matchingValidators)) {
if (mustFindValidator) {
- logger.warn(String.format("Unable to find proper message validator. Message type is '%s' and message payload is '%s'", messageType, message.getPayload(String.class)));
+ if (logger.isWarnEnabled()) {
+ logger.warn(String.format(
+ "Unable to find proper message validator. Message type is '%s' and message payload is '%s'",
+ messageType, message.getPayload(String.class)));
+ }
throw new CitrusRuntimeException("Failed to find proper message validator for message");
}
@@ -308,4 +312,11 @@ public Optional> findSchemaVa
public void setSchemaValidators(Map> schemaValidators) {
this.schemaValidators = schemaValidators;
}
+
+ /**
+ * Return all schema validators.
+ */
+ public Map> getSchemaValidators() {
+ return schemaValidators;
+ }
}
diff --git a/core/citrus-api/src/main/java/org/citrusframework/validation/SchemaValidator.java b/core/citrus-api/src/main/java/org/citrusframework/validation/SchemaValidator.java
index ecc2b006df..3d6a8d11d0 100644
--- a/core/citrus-api/src/main/java/org/citrusframework/validation/SchemaValidator.java
+++ b/core/citrus-api/src/main/java/org/citrusframework/validation/SchemaValidator.java
@@ -88,4 +88,16 @@ static Optional> lookup(Strin
* @return true if the message/message type can be validated by this validator
*/
boolean supportsMessageType(String messageType, Message message);
+
+ /**
+ * @param message the message which is subject of validation
+ * @param schemaValidationEnabled flag to indicate whether schema validation is explicitly enabled
+ * @return true, if the validator can validate the given message
+ */
+ boolean canValidate(Message message, boolean schemaValidationEnabled);
+
+ /**
+ * Validate the message against the given schemaRepository and schema.
+ */
+ void validate(Message message, TestContext context, String schemaRepository, String schema);
}
diff --git a/core/citrus-base/src/main/java/org/citrusframework/actions/SendMessageAction.java b/core/citrus-base/src/main/java/org/citrusframework/actions/SendMessageAction.java
index 20fa11ccff..d91ae3d002 100644
--- a/core/citrus-base/src/main/java/org/citrusframework/actions/SendMessageAction.java
+++ b/core/citrus-base/src/main/java/org/citrusframework/actions/SendMessageAction.java
@@ -16,7 +16,6 @@
package org.citrusframework.actions;
-import org.citrusframework.CitrusSettings;
import org.citrusframework.Completable;
import org.citrusframework.context.TestContext;
import org.citrusframework.endpoint.Endpoint;
@@ -25,16 +24,9 @@
import org.citrusframework.message.MessageBuilder;
import org.citrusframework.message.MessageDirection;
import org.citrusframework.message.MessageProcessor;
-import org.citrusframework.message.MessageType;
import org.citrusframework.message.builder.MessageBuilderSupport;
import org.citrusframework.message.builder.SendMessageBuilderSupport;
-import org.citrusframework.util.IsJsonPredicate;
-import org.citrusframework.util.IsXmlPredicate;
import org.citrusframework.util.StringUtils;
-import org.citrusframework.validation.SchemaValidator;
-import org.citrusframework.validation.context.SchemaValidationContext;
-import org.citrusframework.validation.json.JsonMessageValidationContext;
-import org.citrusframework.validation.xml.XmlMessageValidationContext;
import org.citrusframework.variable.VariableExtractor;
import org.citrusframework.variable.dictionary.DataDictionary;
import org.slf4j.Logger;
@@ -128,7 +120,9 @@ public void doExecute(final TestContext context) {
finished.whenComplete((ctx, ex) -> {
if (ex != null) {
- logger.warn("Failure in forked send action: " + ex.getMessage());
+ if (logger.isWarnEnabled()) {
+ logger.warn("Failure in forked send action: %s".formatted(ex.getMessage()));
+ }
} else {
for (Exception ctxEx : ctx.getExceptions()) {
logger.warn(ctxEx.getMessage());
@@ -146,7 +140,9 @@ public void doExecute(final TestContext context) {
if (StringUtils.hasText(message.getName())) {
context.getMessageStore().storeMessage(message.getName(), message);
} else {
- context.getMessageStore().storeMessage(context.getMessageStore().constructMessageName(this, messageEndpoint), message);
+ context.getMessageStore()
+ .storeMessage(context.getMessageStore().constructMessageName(this, messageEndpoint),
+ message);
}
if (forkMode) {
@@ -179,59 +175,14 @@ public void doExecute(final TestContext context) {
}
/**
- * Validate the message against registered schemas.
- * @param message
+ * Validate the message against registered schema validators.
*/
protected void validateMessage(Message message, TestContext context) {
- List> schemaValidators = null;
- SchemaValidationContext validationContext = null;
- String payload = message.getPayload(String.class);
-
- if ((isSchemaValidation() || isJsonSchemaValidationEnabled()) && IsJsonPredicate.getInstance().test(payload)) {
- schemaValidators = context.getMessageValidatorRegistry()
- .findSchemaValidators(MessageType.JSON.name(), message);
- validationContext = JsonMessageValidationContext.Builder.json()
- .schemaValidation(this.schemaValidation)
- .schema(this.schema)
- .schemaRepository(this.schemaRepository).build();
- } else if ((isSchemaValidation() || isXmlSchemaValidationEnabled()) && IsXmlPredicate.getInstance().test(payload)) {
- schemaValidators = context.getMessageValidatorRegistry()
- .findSchemaValidators(MessageType.XML.name(), message);
- validationContext = XmlMessageValidationContext.Builder.xml()
- .schemaValidation(this.schemaValidation)
- .schema(this.schema)
- .schemaRepository(this.schemaRepository).build();
- }
-
- if (schemaValidators != null) {
- for (SchemaValidator validator : schemaValidators) {
- validator.validate(message, context, validationContext);
- }
- }
-
+ context.getMessageValidatorRegistry().getSchemaValidators().values().stream()
+ .filter(validator -> validator.canValidate(message, isSchemaValidation())).forEach(validator ->
+ validator.validate(message, context, this.schemaRepository, this.schema));
}
- /**
- * Get setting to determine if json schema validation is enabled by default.
- * @return
- */
- private static boolean isJsonSchemaValidationEnabled() {
- return Boolean.getBoolean(CitrusSettings.OUTBOUND_SCHEMA_VALIDATION_ENABLED_PROPERTY)
- || Boolean.getBoolean(CitrusSettings.OUTBOUND_JSON_SCHEMA_VALIDATION_ENABLED_PROPERTY)
- || Boolean.parseBoolean(System.getenv(CitrusSettings.OUTBOUND_SCHEMA_VALIDATION_ENABLED_ENV))
- || Boolean.parseBoolean(System.getenv(CitrusSettings.OUTBOUND_JSON_SCHEMA_VALIDATION_ENABLED_ENV));
- }
-
- /**
- * Get setting to determine if xml schema validation is enabled by default.
- * @return
- */
- private static boolean isXmlSchemaValidationEnabled() {
- return Boolean.getBoolean(CitrusSettings.OUTBOUND_SCHEMA_VALIDATION_ENABLED_PROPERTY)
- || Boolean.getBoolean(CitrusSettings.OUTBOUND_XML_SCHEMA_VALIDATION_ENABLED_PROPERTY)
- || Boolean.parseBoolean(System.getenv(CitrusSettings.OUTBOUND_SCHEMA_VALIDATION_ENABLED_ENV))
- || Boolean.parseBoolean(System.getenv(CitrusSettings.OUTBOUND_XML_SCHEMA_VALIDATION_ENABLED_ENV));
- }
/**
* {@inheritDoc}
@@ -249,12 +200,13 @@ public boolean isDisabled(TestContext context) {
@Override
public boolean isDone(TestContext context) {
return Optional.ofNullable(finished)
- .map(future -> future.isDone() || isDisabled(context))
- .orElseGet(() -> isDisabled(context));
+ .map(future -> future.isDone() || isDisabled(context))
+ .orElseGet(() -> isDisabled(context));
}
/**
* Create message to be sent.
+ *
* @param context
* @param messageType
* @return
@@ -264,7 +216,7 @@ protected Message createMessage(TestContext context, String messageType) {
if (message.getPayload() != null) {
context.getMessageProcessors(MessageDirection.OUTBOUND)
- .forEach(processor -> processor.process(message, context));
+ .forEach(processor -> processor.process(message, context));
if (dataDictionary != null) {
dataDictionary.process(message, context);
@@ -278,6 +230,7 @@ protected Message createMessage(TestContext context, String messageType) {
/**
* Creates or gets the message endpoint instance.
+ *
* @return the message endpoint
*/
public Endpoint getOrCreateEndpoint(TestContext context) {
@@ -292,6 +245,7 @@ public Endpoint getOrCreateEndpoint(TestContext context) {
/**
* Gets the message endpoint.
+ *
* @return
*/
public Endpoint getEndpoint() {
@@ -300,6 +254,7 @@ public Endpoint getEndpoint() {
/**
* Get
+ *
* @return true if schema validation is active for this message
*/
public boolean isSchemaValidation() {
@@ -308,6 +263,7 @@ public boolean isSchemaValidation() {
/**
* Get the name of the schema repository used for validation
+ *
* @return the schema repository name
*/
public String getSchemaRepository() {
@@ -316,6 +272,7 @@ public String getSchemaRepository() {
/**
* Get the name of the schema used for validation
+ *
* @return the schema
*/
public String getSchema() {
@@ -324,6 +281,7 @@ public String getSchema() {
/**
* Get the variable extractors.
+ *
* @return the variableExtractors
*/
public List getVariableExtractors() {
@@ -332,6 +290,7 @@ public List getVariableExtractors() {
/**
* Obtains the message processors.
+ *
* @return
*/
public List getMessageProcessors() {
@@ -340,6 +299,7 @@ public List getMessageProcessors() {
/**
* Gets the messageBuilder.
+ *
* @return the messageBuilder
*/
public MessageBuilder getMessageBuilder() {
@@ -348,6 +308,7 @@ public MessageBuilder getMessageBuilder() {
/**
* Gets the forkMode.
+ *
* @return the forkMode the forkMode to get.
*/
public boolean isForkMode() {
@@ -356,6 +317,7 @@ public boolean isForkMode() {
/**
* Gets the message type for this receive action.
+ *
* @return the messageType
*/
public String getMessageType() {
@@ -364,6 +326,7 @@ public String getMessageType() {
/**
* Gets the data dictionary.
+ *
* @return
*/
public DataDictionary> getDataDictionary() {
@@ -372,6 +335,7 @@ public DataDictionary> getDataDictionary() {
/**
* Gets the endpoint uri.
+ *
* @return
*/
public String getEndpointUri() {
@@ -381,10 +345,12 @@ public String getEndpointUri() {
/**
* Action builder.
*/
- public static class Builder extends SendMessageActionBuilder {
+ public static final class Builder extends
+ SendMessageActionBuilder {
/**
* Fluent API action building entry method used in Java DSL.
+ *
* @return
*/
public static Builder send() {
@@ -393,6 +359,7 @@ public static Builder send() {
/**
* Fluent API action building entry method used in Java DSL.
+ *
* @param messageEndpoint
* @return
*/
@@ -404,6 +371,7 @@ public static Builder send(Endpoint messageEndpoint) {
/**
* Fluent API action building entry method used in Java DSL.
+ *
* @param messageEndpointUri
* @return
*/
@@ -428,7 +396,8 @@ public SendMessageAction doBuild() {
}
- public static class SendMessageActionBuilderSupport extends SendMessageBuilderSupport {
+ public static class SendMessageActionBuilderSupport extends
+ SendMessageBuilderSupport {
public SendMessageActionBuilderSupport(SendMessageAction.Builder delegate) {
super(delegate);
@@ -438,14 +407,15 @@ public SendMessageActionBuilderSupport(SendMessageAction.Builder delegate) {
/**
* Base send message action builder also used by subclasses of base send message action.
*/
- public static abstract class SendMessageActionBuilder, B extends SendMessageActionBuilder>
- extends MessageBuilderSupport.MessageActionBuilder {
+ public abstract static class SendMessageActionBuilder, B extends SendMessageActionBuilder>
+ extends MessageBuilderSupport.MessageActionBuilder {
protected boolean forkMode = false;
protected CompletableFuture finished;
/**
* Sets the fork mode for this send action builder.
+ *
* @param forkMode
* @return
*/
@@ -463,7 +433,8 @@ public final T build() {
if (referenceResolver != null) {
if (messageBuilderSupport.getDataDictionaryName() != null) {
this.messageBuilderSupport.dictionary(
- referenceResolver.resolve(messageBuilderSupport.getDataDictionaryName(), DataDictionary.class));
+ referenceResolver.resolve(messageBuilderSupport.getDataDictionaryName(),
+ DataDictionary.class));
}
}
diff --git a/endpoints/citrus-http/src/main/java/org/citrusframework/http/actions/HttpClientRequestActionBuilder.java b/endpoints/citrus-http/src/main/java/org/citrusframework/http/actions/HttpClientRequestActionBuilder.java
index cced421b22..479c6da50d 100644
--- a/endpoints/citrus-http/src/main/java/org/citrusframework/http/actions/HttpClientRequestActionBuilder.java
+++ b/endpoints/citrus-http/src/main/java/org/citrusframework/http/actions/HttpClientRequestActionBuilder.java
@@ -17,7 +17,6 @@
package org.citrusframework.http.actions;
import jakarta.servlet.http.Cookie;
-
import org.citrusframework.actions.SendMessageAction;
import org.citrusframework.http.message.HttpMessage;
import org.citrusframework.http.message.HttpMessageBuilder;
@@ -57,11 +56,15 @@ protected HttpClientRequestActionBuilder(MessageBuilder messageBuilder, HttpMess
@Override
public HttpMessageBuilderSupport getMessageBuilderSupport() {
if (messageBuilderSupport == null) {
- messageBuilderSupport = new HttpMessageBuilderSupport(httpMessage, this);
+ messageBuilderSupport = createHttpMessageBuilderSupport();
}
return super.getMessageBuilderSupport();
}
+ protected HttpMessageBuilderSupport createHttpMessageBuilderSupport() {
+ return new HttpMessageBuilderSupport(httpMessage, this);
+ }
+
/**
* Sets the request path.
* @param path
diff --git a/endpoints/citrus-http/src/main/java/org/citrusframework/http/actions/HttpClientResponseActionBuilder.java b/endpoints/citrus-http/src/main/java/org/citrusframework/http/actions/HttpClientResponseActionBuilder.java
index 292caf3016..3929df439c 100644
--- a/endpoints/citrus-http/src/main/java/org/citrusframework/http/actions/HttpClientResponseActionBuilder.java
+++ b/endpoints/citrus-http/src/main/java/org/citrusframework/http/actions/HttpClientResponseActionBuilder.java
@@ -17,6 +17,7 @@
package org.citrusframework.http.actions;
import jakarta.servlet.http.Cookie;
+import java.util.Optional;
import org.citrusframework.actions.ReceiveMessageAction;
import org.citrusframework.http.message.HttpMessage;
import org.citrusframework.http.message.HttpMessageBuilder;
@@ -26,8 +27,6 @@
import org.citrusframework.message.builder.ReceiveMessageBuilderSupport;
import org.springframework.http.HttpStatusCode;
-import java.util.Optional;
-
/**
* @since 2.4
*/
@@ -59,11 +58,15 @@ public HttpClientResponseActionBuilder(MessageBuilder messageBuilder, HttpMessag
@Override
public HttpMessageBuilderSupport getMessageBuilderSupport() {
if (messageBuilderSupport == null) {
- messageBuilderSupport = new HttpMessageBuilderSupport(httpMessage, this);
+ messageBuilderSupport = createHttpMessageBuilderSupport();
}
return super.getMessageBuilderSupport();
}
+ protected HttpMessageBuilderSupport createHttpMessageBuilderSupport() {
+ return new HttpMessageBuilderSupport(httpMessage, this);
+ }
+
public static class HttpMessageBuilderSupport extends ReceiveMessageBuilderSupport {
private final HttpMessage httpMessage;
diff --git a/endpoints/citrus-http/src/main/java/org/citrusframework/http/actions/HttpServerRequestActionBuilder.java b/endpoints/citrus-http/src/main/java/org/citrusframework/http/actions/HttpServerRequestActionBuilder.java
index 39f22cc090..148af48b6a 100644
--- a/endpoints/citrus-http/src/main/java/org/citrusframework/http/actions/HttpServerRequestActionBuilder.java
+++ b/endpoints/citrus-http/src/main/java/org/citrusframework/http/actions/HttpServerRequestActionBuilder.java
@@ -16,9 +16,8 @@
package org.citrusframework.http.actions;
-import java.util.Optional;
-
import jakarta.servlet.http.Cookie;
+import java.util.Optional;
import org.citrusframework.actions.ReceiveMessageAction;
import org.citrusframework.http.message.HttpMessage;
import org.citrusframework.http.message.HttpMessageBuilder;
@@ -61,11 +60,15 @@ public HttpServerRequestActionBuilder(MessageBuilder messageBuilder, HttpMessage
@Override
public HttpMessageBuilderSupport getMessageBuilderSupport() {
if (messageBuilderSupport == null) {
- messageBuilderSupport = new HttpMessageBuilderSupport(httpMessage, this);
+ messageBuilderSupport = createMessageBuilderSupport();
}
return super.getMessageBuilderSupport();
}
+ protected HttpMessageBuilderSupport createMessageBuilderSupport() {
+ return new HttpMessageBuilderSupport(httpMessage, this);
+ }
+
/**
* Sets the request path.
* @param path
diff --git a/endpoints/citrus-http/src/main/java/org/citrusframework/http/actions/HttpServerResponseActionBuilder.java b/endpoints/citrus-http/src/main/java/org/citrusframework/http/actions/HttpServerResponseActionBuilder.java
index e0610ce5d0..8a0ed55d59 100644
--- a/endpoints/citrus-http/src/main/java/org/citrusframework/http/actions/HttpServerResponseActionBuilder.java
+++ b/endpoints/citrus-http/src/main/java/org/citrusframework/http/actions/HttpServerResponseActionBuilder.java
@@ -57,11 +57,15 @@ public HttpServerResponseActionBuilder(MessageBuilder messageBuilder, HttpMessag
@Override
public HttpMessageBuilderSupport getMessageBuilderSupport() {
if (messageBuilderSupport == null) {
- messageBuilderSupport = new HttpMessageBuilderSupport(httpMessage, this);
+ messageBuilderSupport = createMessageBuilderSupport();
}
return super.getMessageBuilderSupport();
}
+ protected HttpMessageBuilderSupport createMessageBuilderSupport() {
+ return new HttpMessageBuilderSupport(httpMessage, this);
+ }
+
public static class HttpMessageBuilderSupport extends SendMessageBuilderSupport {
private final HttpMessage httpMessage;
diff --git a/pom.xml b/pom.xml
index f915f68c37..0d4820b6d3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -195,7 +195,7 @@
1.10.15
4.8.0
1.1.27
- com.atlassian.oai
+ 2.41.0
1.8.0
3.26.3
4.2.2
diff --git a/test-api-generator/pom.xml b/test-api-generator/pom.xml
index b33d0f717e..c3776c4931 100644
--- a/test-api-generator/pom.xml
+++ b/test-api-generator/pom.xml
@@ -38,6 +38,7 @@
${junit.jupiter.version}
test