diff --git a/connectors/citrus-openapi/pom.xml b/connectors/citrus-openapi/pom.xml index bf85267d78..83d9212ec5 100644 --- a/connectors/citrus-openapi/pom.xml +++ b/connectors/citrus-openapi/pom.xml @@ -72,6 +72,12 @@ ${project.version} test + + org.assertj + assertj-core + ${assertj.version} + test + diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/OpenApiTestDataGenerator.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/OpenApiTestDataGenerator.java index 900897fe4b..87f0bc79c1 100644 --- a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/OpenApiTestDataGenerator.java +++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/OpenApiTestDataGenerator.java @@ -26,6 +26,8 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import static org.citrusframework.openapi.model.OasModelHelper.*; + /** * Generates proper payloads and validation expressions based on Open API specification rules. Creates outbound payloads * with generated random test data according to specification and creates inbound payloads with proper validation expressions to @@ -37,19 +39,23 @@ public class OpenApiTestDataGenerator { /** * Creates payload from schema for outbound message. + * * @param schema * @param definitions * @return */ - public static String createOutboundPayload(OasSchema schema, Map definitions, - OpenApiSpecification specification) { - if (OasModelHelper.isReferenceType(schema)) { - OasSchema resolved = definitions.get(OasModelHelper.getReferenceName(schema.$ref)); + public static String createOutboundPayload( + OasSchema schema, + Map definitions, + OpenApiSpecification specification + ) { + if (isReferenceType(schema)) { + OasSchema resolved = definitions.get(getReferenceName(schema.$ref)); return createOutboundPayload(resolved, definitions, specification); } StringBuilder payload = new StringBuilder(); - if (OasModelHelper.isObjectType(schema)) { + if (isObjectType(schema)) { payload.append("{"); if (schema.properties != null) { @@ -80,15 +86,72 @@ public static String createOutboundPayload(OasSchema schema, Map definitions, + OpenApiSpecification specification + ) { + if (isReferenceType(schema)) { + OasSchema resolved = definitions.get(getReferenceName(schema.$ref)); + return createInboundPayload(resolved, definitions, specification); + } + + StringBuilder payload = new StringBuilder(); + if (isObjectType(schema)) { + payload.append("{"); + + if (schema.properties != null) { + for (Map.Entry entry : schema.properties.entrySet()) { + if (specification.isValidateOptionalFields() || isRequired(schema, entry.getKey())) { + payload.append("\"") + .append(entry.getKey()) + .append("\": ") + .append(createValidationExpression(entry.getValue(), definitions, true, specification)) + .append(","); + } + } + } + + if (payload.toString().endsWith(",")) { + payload.replace(payload.length() - 1, payload.length(), ""); + } + + payload.append("}"); + } else if (OasModelHelper.isArrayType(schema)) { + payload.append("["); + payload.append(createValidationExpression((OasSchema) schema.items, definitions, true, specification)); + payload.append("]"); + } else { + payload.append(createValidationExpression(schema, definitions, false, specification)); + } + + return payload.toString(); + } + /** * Use test variable with given name if present or create value from schema with random values + * * @param schema * @param definitions * @param quotes * @return */ - public static String createRandomValueExpression(String name, OasSchema schema, Map definitions, - boolean quotes, OpenApiSpecification specification, TestContext context) { + @Deprecated(forRemoval = true) + public static String createRandomValueExpression( + String name, + OasSchema schema, + Map definitions, + boolean quotes, + OpenApiSpecification specification, + TestContext context + ) { if (context.getVariables().containsKey(name)) { return CitrusSettings.VARIABLE_PREFIX + name + CitrusSettings.VARIABLE_SUFFIX; } @@ -98,20 +161,25 @@ public static String createRandomValueExpression(String name, OasSchema schema, /** * Create payload from schema with random values. + * * @param schema * @param definitions * @param quotes * @return */ - public static String createRandomValueExpression(OasSchema schema, Map definitions, boolean quotes, - OpenApiSpecification specification) { - if (OasModelHelper.isReferenceType(schema)) { - OasSchema resolved = definitions.get(OasModelHelper.getReferenceName(schema.$ref)); + public static String createRandomValueExpression( + OasSchema schema, + Map definitions, + boolean quotes, + OpenApiSpecification specification + ) { + if (isReferenceType(schema)) { + OasSchema resolved = definitions.get(getReferenceName(schema.$ref)); return createRandomValueExpression(resolved, definitions, quotes, specification); } StringBuilder payload = new StringBuilder(); - if (OasModelHelper.isObjectType(schema) || OasModelHelper.isArrayType(schema)) { + if (isObjectType(schema) || OasModelHelper.isArrayType(schema)) { payload.append(createOutboundPayload(schema, definitions, specification)); } else if ("string".equals(schema.type)) { if (quotes) { @@ -147,57 +215,71 @@ public static String createRandomValueExpression(OasSchema schema, Map definitions, - OpenApiSpecification specification) { - if (OasModelHelper.isReferenceType(schema)) { - OasSchema resolved = definitions.get(OasModelHelper.getReferenceName(schema.$ref)); - return createInboundPayload(resolved, definitions, specification); + public static String createRandomValueExpression( + String name, + OasSchema schema, + TestContext context + ) { + if (context.getVariables().containsKey(name)) { + return CitrusSettings.VARIABLE_PREFIX + name + CitrusSettings.VARIABLE_SUFFIX; } - StringBuilder payload = new StringBuilder(); - if (OasModelHelper.isObjectType(schema)) { - payload.append("{"); + return createRandomValueExpression(schema); + } - if (schema.properties != null) { - for (Map.Entry entry : schema.properties.entrySet()) { - if (specification.isValidateOptionalFields() || isRequired(schema, entry.getKey())) { - payload.append("\"") - .append(entry.getKey()) - .append("\": ") - .append(createValidationExpression(entry.getValue(), definitions, true, specification)) - .append(","); - } + /** + * Create random value expression using functions according to schema type and format. + * + * @param schema + * @return + */ + public static String createRandomValueExpression( + OasSchema schema + ) { + switch (schema.type) { + case "string": + if (schema.format != null && schema.format.equals("date")) { + return "\"citrus:currentDate('yyyy-MM-dd')\""; + } else if (schema.format != null && schema.format.equals("date-time")) { + return "\"citrus:currentDate('yyyy-MM-dd'T'hh:mm:ss')\""; + } else if (StringUtils.hasText(schema.pattern)) { + return "\"citrus:randomValue(" + schema.pattern + ")\""; + } else if (!CollectionUtils.isEmpty(schema.enum_)) { + return "\"citrus:randomEnumValue(" + (String.join(",", schema.enum_)) + ")\""; + } else if (schema.format != null && schema.format.equals("uuid")) { + return "citrus:randomUUID()"; + } else { + return "citrus:randomString(10)"; } - } - - if (payload.toString().endsWith(",")) { - payload.replace(payload.length() - 1, payload.length(), ""); - } - - payload.append("}"); - } else if (OasModelHelper.isArrayType(schema)) { - payload.append("["); - payload.append(createValidationExpression((OasSchema) schema.items, definitions, true, specification)); - payload.append("]"); - } else { - payload.append(createValidationExpression(schema, definitions, false, specification)); + case "number": + case "integer": + return "citrus:randomNumber(8)"; + case "boolean": + return "citrus:randomEnumValue('true', 'false')"; + default: + return ""; } - - return payload.toString(); } /** * Checks if given field name is in list of required fields for this schema. + * * @param schema * @param field * @return */ - private static boolean isRequired(OasSchema schema, String field) { + private static boolean isRequired( + OasSchema schema, + String field + ) { if (schema.required == null) { return true; } @@ -207,6 +289,7 @@ private static boolean isRequired(OasSchema schema, String field) { /** * Use test variable with given name if present or create validation expression using functions according to schema type and format. + * * @param name * @param schema * @param definitions @@ -214,9 +297,14 @@ private static boolean isRequired(OasSchema schema, String field) { * @param context * @return */ - public static String createValidationExpression(String name, OasSchema schema, Map definitions, - boolean quotes, OpenApiSpecification specification, - TestContext context) { + public static String createValidationExpression( + String name, + OasSchema schema, + Map definitions, + boolean quotes, + OpenApiSpecification specification, + TestContext context + ) { if (context.getVariables().containsKey(name)) { return CitrusSettings.VARIABLE_PREFIX + name + CitrusSettings.VARIABLE_SUFFIX; } @@ -226,20 +314,25 @@ public static String createValidationExpression(String name, OasSchema schema, M /** * Create validation expression using functions according to schema type and format. + * * @param schema * @param definitions * @param quotes * @return */ - public static String createValidationExpression(OasSchema schema, Map definitions, boolean quotes, - OpenApiSpecification specification) { - if (OasModelHelper.isReferenceType(schema)) { - OasSchema resolved = definitions.get(OasModelHelper.getReferenceName(schema.$ref)); + public static String createValidationExpression( + OasSchema schema, + Map definitions, + boolean quotes, + OpenApiSpecification specification + ) { + if (isReferenceType(schema)) { + OasSchema resolved = definitions.get(getReferenceName(schema.$ref)); return createValidationExpression(resolved, definitions, quotes, specification); } StringBuilder payload = new StringBuilder(); - if (OasModelHelper.isObjectType(schema)) { + if (isObjectType(schema)) { payload.append("{"); if (schema.properties != null) { @@ -276,10 +369,13 @@ public static String createValidationExpression(OasSchema schema, Map parameters = new HashMap<>(); + + public OpenApiOperationBuilder(String operationId) { + this.operationId = operationId; + } + + public static OpenApiOperationBuilder operation(String operationId) { + return new OpenApiOperationBuilder(operationId); + } + + public OpenApiOperationBuilder withParameter(String name, Object value) { + parameters.put(name, value); + return this; + } + + public OpenApiOperationBuilder withParameters(Map parameters) { + this.parameters.putAll(parameters); + return this; + } + } + + public OpenApiClientRequestActionBuilder send(OpenApiOperationBuilder openApiOperationBuilder) { + var builder = OpenApiClientRequestActionBuilder.create( + specification, + openApiOperationBuilder.operationId + ); if (httpClient != null) { builder.endpoint(httpClient); } else { @@ -73,11 +99,19 @@ public OpenApiClientRequestActionBuilder send(String operationId) { builder.name("openapi:send-request"); builder.withReferenceResolver(referenceResolver); + openApiOperationBuilder.parameters.forEach(builder.getMessageBuilder()::withParameter); this.delegate = builder; return builder; } + /** + * Sends Http requests as client. + */ + public OpenApiClientRequestActionBuilder send(String operationId) { + return send(OpenApiOperationBuilder.operation(operationId)); + } + /** * Receives Http response messages as client. * Uses default Http status 200 OK. diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientRequestActionBuilder.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientRequestActionBuilder.java index da387df437..6671bf3d33 100644 --- a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientRequestActionBuilder.java +++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientRequestActionBuilder.java @@ -16,17 +16,7 @@ package org.citrusframework.openapi.actions; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.regex.Pattern; - -import io.apicurio.datamodels.openapi.models.OasDocument; -import io.apicurio.datamodels.openapi.models.OasOperation; -import io.apicurio.datamodels.openapi.models.OasParameter; -import io.apicurio.datamodels.openapi.models.OasPathItem; -import io.apicurio.datamodels.openapi.models.OasSchema; +import io.apicurio.datamodels.openapi.models.*; import org.citrusframework.CitrusSettings; import org.citrusframework.context.TestContext; import org.citrusframework.exceptions.CitrusRuntimeException; @@ -35,113 +25,168 @@ import org.citrusframework.http.message.HttpMessageBuilder; import org.citrusframework.message.Message; import org.citrusframework.openapi.OpenApiSpecification; -import org.citrusframework.openapi.OpenApiTestDataGenerator; import org.citrusframework.openapi.model.OasModelHelper; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import java.util.*; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import static java.lang.Boolean.TRUE; +import static org.citrusframework.CitrusSettings.VARIABLE_PREFIX; +import static org.citrusframework.CitrusSettings.VARIABLE_SUFFIX; +import static org.citrusframework.openapi.OpenApiTestDataGenerator.createOutboundPayload; +import static org.citrusframework.openapi.OpenApiTestDataGenerator.createRandomValueExpression; +import static org.citrusframework.openapi.model.OasModelHelper.*; + /** * @author Christoph Deppisch * @since 4.1 */ public class OpenApiClientRequestActionBuilder extends HttpClientRequestActionBuilder { - /** - * Default constructor initializes http request message builder. - */ - public OpenApiClientRequestActionBuilder(OpenApiSpecification openApiSpec, String operationId) { - this(new HttpMessage(), openApiSpec, operationId); + // TODO remove? + public OpenApiClientRequestMessageBuilder getMessageBuilder() { + return messageBuilder; + } + + private final OpenApiClientRequestMessageBuilder messageBuilder; + + protected OpenApiClientRequestActionBuilder(OpenApiClientRequestMessageBuilder messageBuilder) { + super(messageBuilder, messageBuilder.httpMessage); + this.messageBuilder = messageBuilder; } - public OpenApiClientRequestActionBuilder(HttpMessage httpMessage, OpenApiSpecification openApiSpec, - String operationId) { - super(new OpenApiClientRequestMessageBuilder(httpMessage, openApiSpec, operationId), httpMessage); + public static OpenApiClientRequestActionBuilder create(OpenApiSpecification openApiSpec, String operationId) { + var messageBuilder = new OpenApiClientRequestMessageBuilder(new HttpMessage(), openApiSpec, operationId, Map.of()); + return new OpenApiClientRequestActionBuilder(messageBuilder); } - private static class OpenApiClientRequestMessageBuilder extends HttpMessageBuilder { + protected static class OpenApiClientRequestMessageBuilder extends HttpMessageBuilder { + private final Map parameters = new HashMap<>(); private final OpenApiSpecification openApiSpec; private final String operationId; private final HttpMessage httpMessage; - public OpenApiClientRequestMessageBuilder(HttpMessage httpMessage, OpenApiSpecification openApiSpec, - String operationId) { + public OpenApiClientRequestMessageBuilder( + HttpMessage httpMessage, + OpenApiSpecification openApiSpec, + String operationId, + Map parameters + ) { super(httpMessage); this.openApiSpec = openApiSpec; this.operationId = operationId; this.httpMessage = httpMessage; + this.parameters.putAll(parameters); + } + + private record OasItem( + OasOperation operation, + OasPathItem pathItem, + HttpMethod method + ) { + public static OasItem create(String operationId, OasDocument oasDocument) { + OasItem item = null; + + for (OasPathItem path : OasModelHelper.getPathItems(oasDocument.paths)) { + Optional> operationEntry = OasModelHelper.getOperationMap(path).entrySet().stream() + .filter(op -> operationId.equals(op.getValue().operationId)) + .findFirst(); + + if (operationEntry.isPresent()) { + item = new OasItem( + operationEntry.get().getValue(), + path, + HttpMethod.valueOf(operationEntry.get().getKey().toUpperCase(Locale.US)) + ); + break; + } + } + + if (item == null) { + throw new CitrusRuntimeException( + "Unable to locate operation with id '%s' in OpenAPI specification %s" + .formatted(operationId, oasDocument.getAttributeNames()) + ); + } + return item; + } + } + + public OpenApiClientRequestMessageBuilder withParameter(String name, Object number) { + parameters.put(name, number); + return this; } @Override public Message build(TestContext context, String messageType) { // TODO: TAT-1291 - make parameter substitution more explicit? + context.addVariables(parameters); OasDocument oasDocument = openApiSpec.getOpenApiDoc(context); - OasOperation operation = null; - OasPathItem pathItem = null; - HttpMethod method = null; - - for (OasPathItem path : OasModelHelper.getPathItems(oasDocument.paths)) { - Optional> operationEntry = OasModelHelper.getOperationMap(path).entrySet().stream() - .filter(op -> operationId.equals(op.getValue().operationId)) - .findFirst(); - - if (operationEntry.isPresent()) { - method = HttpMethod.valueOf(operationEntry.get().getKey().toUpperCase(Locale.US)); - operation = operationEntry.get().getValue(); - pathItem = path; - break; - } - } - - if (operation == null) { - throw new CitrusRuntimeException("Unable to locate operation with id '%s' in OpenAPI specification %s".formatted(operationId, openApiSpec.getSpecUrl())); - } + var item = OasItem.create(operationId, oasDocument); + + getRequiredOrPresentParametersIn("header", item, context).forEach(param -> + httpMessage.setHeader( + param.getName(), + context.getVariables().containsKey(param.getName()) + ? CitrusSettings.VARIABLE_PREFIX + param.getName() + CitrusSettings.VARIABLE_SUFFIX + : + createRandomValueExpression( + (OasSchema) param.schema, + getSchemaDefinitions(oasDocument), + false, + openApiSpec + ) + ) + ); + + getRequiredOrPresentParametersIn("query", item, context) + .forEach(param -> httpMessage.queryParam( + param.getName(), + createRandomValueExpression(param.getName(), (OasSchema) param.schema, context) + )); + + getRequestBodySchema(oasDocument, item.operation) + .ifPresent(oasSchema -> httpMessage.setPayload( + createOutboundPayload(oasSchema, getSchemaDefinitions(oasDocument), openApiSpec) + )); + + getRequestContentType(item.operation).ifPresent(httpMessage::contentType); + httpMessage.path(getSubstitutedPath(context, item)); + httpMessage.method(item.method); - if (operation.parameters != null) { - operation.parameters.stream() - .filter(param -> "header".equals(param.in)) - .filter(param -> (param.required != null && param.required) || context.getVariables().containsKey(param.getName())) - .forEach(param -> httpMessage.setHeader(param.getName(), - OpenApiTestDataGenerator.createRandomValueExpression(param.getName(), (OasSchema) param.schema, - OasModelHelper.getSchemaDefinitions(oasDocument), false, openApiSpec, context))); - - operation.parameters.stream() - .filter(param -> "query".equals(param.in)) - .filter(param -> (param.required != null && param.required) || context.getVariables().containsKey(param.getName())) - .forEach(param -> httpMessage.queryParam(param.getName(), - OpenApiTestDataGenerator.createRandomValueExpression(param.getName(), (OasSchema) param.schema, context))); - } + return super.build(context, messageType); + } - Optional body = OasModelHelper.getRequestBodySchema(oasDocument, operation); - body.ifPresent(oasSchema -> httpMessage.setPayload(OpenApiTestDataGenerator.createOutboundPayload(oasSchema, - OasModelHelper.getSchemaDefinitions(oasDocument), openApiSpec))); - - String randomizedPath = pathItem.getPath(); - if (operation.parameters != null) { - List pathParams = operation.parameters.stream() - .filter(p -> "path".equals(p.in)).toList(); - - for (OasParameter parameter : pathParams) { - String parameterValue; - if (context.getVariables().containsKey(parameter.getName())) { - parameterValue = "\\" + CitrusSettings.VARIABLE_PREFIX + parameter.getName() + CitrusSettings.VARIABLE_SUFFIX; - } else { - parameterValue = OpenApiTestDataGenerator.createRandomValueExpression((OasSchema) parameter.schema); - } - randomizedPath = Pattern.compile("\\{" + parameter.getName() + "}") - .matcher(randomizedPath) - .replaceAll(parameterValue); - } - } + private static Stream getRequiredOrPresentParametersIn(String header, OasItem item, TestContext context) { + return item.operation.getParametersIn(header).stream() + .filter(onlyRequiredOrPresentParameters(context)); + } - OasModelHelper.getRequestContentType(operation) - .ifPresent(contentType -> httpMessage.setHeader(HttpHeaders.CONTENT_TYPE, contentType)); + private static Predicate onlyRequiredOrPresentParameters(TestContext context) { + return param -> TRUE.equals(param.required) || context.getVariables().containsKey(param.getName()); + } - httpMessage.path(randomizedPath); - httpMessage.method(method); + private static String getSubstitutedPath(TestContext context, OasItem item) { + String randomizedPath = item.pathItem.getPath(); + List pathParams = item.operation.getParametersIn("path"); - return super.build(context, messageType); + for (OasParameter parameter : pathParams) { + String parameterValue; + if (context.getVariables().containsKey(parameter.getName())) { + parameterValue = "\\" + VARIABLE_PREFIX + parameter.getName() + VARIABLE_SUFFIX; + } else { + parameterValue = createRandomValueExpression((OasSchema) parameter.schema); + } + randomizedPath = Pattern.compile("\\{" + parameter.getName() + "}") + .matcher(randomizedPath) + .replaceAll(parameterValue); + } + return randomizedPath; } } } 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 6f61edfa68..5ccf83d612 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 @@ -27,11 +27,13 @@ import org.citrusframework.testng.spring.TestNGCitrusSpringSupport; import org.citrusframework.util.SocketUtils; import org.springframework.http.HttpStatus; +import org.testng.annotations.Ignore; import org.testng.annotations.Test; import static org.citrusframework.http.actions.HttpActionBuilder.http; import static org.citrusframework.message.MessageType.XML; import static org.citrusframework.openapi.actions.OpenApiActionBuilder.openapi; +import static org.citrusframework.openapi.actions.OpenApiClientActionBuilder.OpenApiOperationBuilder.operation; /** * @author Christoph Deppisch @@ -60,16 +62,119 @@ public class OpenApiClientIT extends TestNGCitrusSpringSupport { @CitrusTest public void getPetById() { variable("petId", "1001"); + variable("verbose", "true"); + variable("correlationIds", "1234abcd"); + + when(openapi(petstoreSpec) + .client(httpClient) + .send("getPetById") + .fork(true) + .message() + ); + + then(http().server(httpServer) + .receive() + .get("/pet/1001") + .queryParam("verbose", "true") + .message() + // TODO bug? - cannot check correlationId + // see: org/citrusframework/validation/DefaultMessageHeaderValidator.java:68 + // see: org.citrusframework.message.MessageHeaderUtils.isSpringInternalHeader + .header("correlationIds", "1234abcd") + .accept("@contains('application/json')@") + ); + + then(http().server(httpServer) + .send() + .response(HttpStatus.OK) + .message() + .body(Resources.create("classpath:org/citrusframework/openapi/petstore/pet.json")) + .contentType("application/json")); + + then(openapi(petstoreSpec) + .client(httpClient) + .receive("getPetById", HttpStatus.OK)); + } + + @CitrusTest + public void getPetById_requiredParamsShouldBeGeneratedIfNotProvided() { when(openapi(petstoreSpec) .client(httpClient) .send("getPetById") + .fork(true) + ); + + then(http().server(httpServer) + .receive() + .get("@matches('/pet/\\d+')@") + .message() + // TODO bug? - cannot check correlationId + // see: org/citrusframework/validation/DefaultMessageHeaderValidator.java:68 + // see: org.citrusframework.message.MessageHeaderUtils.isSpringInternalHeader + // .header("correlationId", "@matches('\\w+')@") + ); + + // TODO does not work without..?! + variable("petId", "1001"); + then(http().server(httpServer) + .send() + .response(HttpStatus.OK) + .message() + .body(Resources.create("classpath:org/citrusframework/openapi/petstore/pet.json")) + .contentType("application/json")); + + then(openapi(petstoreSpec) + .client(httpClient) + .receive("getPetById", HttpStatus.OK)); + } + + @CitrusTest + public void getPetById_setParameterExplicitly() { + when(openapi(petstoreSpec) + .client(httpClient) + .send(operation("getPetById") + .withParameter("petId", "1001") + .withParameter("correlationIds", "5599") + .withParameter("verbose", "true")) .fork(true)); then(http().server(httpServer) .receive() - .get("/pet/${petId}") + .get("/pet/1001") + .message() + .queryParam("verbose", "true") + .header("correlationIds", "5599") + .accept("@contains('application/json')@")); + + then(http().server(httpServer) + .send() + .response(HttpStatus.OK) + .message() + .body(Resources.create("classpath:org/citrusframework/openapi/petstore/pet.json")) + .contentType("application/json")); + + then(openapi(petstoreSpec) + .client(httpClient) + .receive("getPetById", HttpStatus.OK)); + } + + @CitrusTest + public void getPetById_generated() { + when(openapi(petstoreSpec) + .client(httpClient) + .send(operation("getPetById") + .withParameter("petId", "1001") + .withParameter("correlationIds", "5599") + .withParameter("verbose", "true")) + .fork(true)); + + then(http().server(httpServer) + .receive() + .get("/pet/1001") .message() + .queryParam("verbose", "true") + .header("correlationIds", "5599") .accept("@contains('application/json')@")); then(http().server(httpServer) @@ -84,7 +189,43 @@ public void getPetById() { .receive("getPetById", HttpStatus.OK)); } + /* TODO create issues + @CitrusTest + public void BUG_getPetById_paramsCanAlsoBeSetWithMessageBuilder() { + variable("petId", "1001"); + + when(openapi(petstoreSpec) + .client(httpClient) + .send("getPetById") + .fork(true) + .message() + // TODO Bug - if the params are already set on the message, they get overwritten + .queryParam("verbose", "false") + .header("correlationIds", "1234F5gXW") + ); + + then(http().server(httpServer) + .receive() + .get("/pet/1001") + .queryParam("verbose", "true") + .message() + .header("correlationIds", "1234F5gXW") + ); + + then(http().server(httpServer) + .send() + .response(HttpStatus.OK) + .message() + .body(Resources.create("classpath:org/citrusframework/openapi/petstore/pet.json")) + .contentType("application/json")); + + then(openapi(petstoreSpec) + .client(httpClient) + .receive("getPetById", HttpStatus.OK)); + } + @CitrusTest + @Ignore public void BUG_should_be_possible_to_switch_content_type__to_xml() { variable("petId", "1001"); @@ -125,6 +266,7 @@ public void BUG_should_be_possible_to_switch_content_type__to_xml() { } @CitrusTest + @Ignore public void BUG_should_only_validate_the_presence_of_required_properties() { variable("petId", "1001"); @@ -161,9 +303,10 @@ public void BUG_should_only_validate_the_presence_of_required_properties() { .message() ); } + */ @CitrusTest - public void getAddPet() { + public void postAddPet() { variable("petId", "1001"); when(openapi(petstoreSpec) 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 a6c383d93a..6e0150af94 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 @@ -16,11 +16,6 @@ package org.citrusframework.openapi.yaml; -import java.io.IOException; -import java.util.Map; -import java.util.Queue; -import java.util.concurrent.ArrayBlockingQueue; - import org.citrusframework.TestActor; import org.citrusframework.TestCase; import org.citrusframework.TestCaseMetaInfo; @@ -29,7 +24,6 @@ import org.citrusframework.endpoint.EndpointAdapter; import org.citrusframework.endpoint.direct.DirectEndpointAdapter; import org.citrusframework.endpoint.direct.DirectSyncEndpointConfiguration; -import org.citrusframework.endpoint.resolver.EndpointUriResolver; import org.citrusframework.http.client.HttpClient; import org.citrusframework.http.message.HttpMessage; import org.citrusframework.http.message.HttpMessageBuilder; @@ -38,7 +32,6 @@ import org.citrusframework.message.DefaultMessage; import org.citrusframework.message.DefaultMessageQueue; import org.citrusframework.message.Message; -import org.citrusframework.message.MessageHeaders; import org.citrusframework.message.MessageQueue; import org.citrusframework.spi.BindToRegistry; import org.citrusframework.util.SocketUtils; @@ -53,12 +46,23 @@ import org.mockito.Mockito; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; -import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import java.io.IOException; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.citrusframework.endpoint.resolver.EndpointUriResolver.ENDPOINT_URI_HEADER_NAME; +import static org.citrusframework.endpoint.resolver.EndpointUriResolver.REQUEST_PATH_HEADER_NAME; import static org.citrusframework.http.endpoint.builder.HttpEndpoints.http; +import static org.citrusframework.http.message.HttpMessageHeaders.*; +import static org.citrusframework.message.MessageHeaders.ID; +import static org.citrusframework.message.MessageHeaders.TIMESTAMP; +import static org.springframework.http.HttpMethod.GET; /** * @author Christoph Deppisch @@ -148,103 +152,109 @@ public void shouldLoadOpenApiClientActions() throws IOException { testLoader.load(); TestCase result = testLoader.getTestCase(); - Assert.assertEquals(result.getName(), "OpenApiClientTest"); - Assert.assertEquals(result.getMetaInfo().getAuthor(), "Christoph"); - Assert.assertEquals(result.getMetaInfo().getStatus(), TestCaseMetaInfo.Status.FINAL); - Assert.assertEquals(result.getActionCount(), 4L); - Assert.assertEquals(result.getTestAction(0).getClass(), SendMessageAction.class); - Assert.assertEquals(result.getTestAction(0).getName(), "openapi:send-request"); + assertThat(result.getName()).isEqualTo("OpenApiClientTest"); + assertThat(result.getMetaInfo().getAuthor()).isEqualTo("Christoph"); + assertThat(result.getMetaInfo().getStatus()).isEqualTo(TestCaseMetaInfo.Status.FINAL); + assertThat(result.getActions()).hasSize(4); + + assertThat(result.getTestAction(0).getClass()).isEqualTo(SendMessageAction.class); + assertThat(result.getTestAction(0).getName()).isEqualTo("openapi:send-request"); - Assert.assertEquals(result.getTestAction(1).getClass(), ReceiveMessageAction.class); - Assert.assertEquals(result.getTestAction(1).getName(), "openapi:receive-response"); + assertThat(result.getTestAction(1).getClass()).isEqualTo(ReceiveMessageAction.class); + assertThat(result.getTestAction(1).getName()).isEqualTo("openapi:receive-response"); int actionIndex = 0; - SendMessageAction sendMessageAction = (SendMessageAction) result.getTestAction(actionIndex++); - Assert.assertFalse(sendMessageAction.isForkMode()); - Assert.assertTrue(sendMessageAction.getMessageBuilder() instanceof HttpMessageBuilder); - HttpMessageBuilder httpMessageBuilder = ((HttpMessageBuilder)sendMessageAction.getMessageBuilder()); - Assert.assertNotNull(httpMessageBuilder); - Assert.assertEquals(httpMessageBuilder.buildMessagePayload(context, sendMessageAction.getMessageType()), ""); - Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().size(), 5L); - Assert.assertNotNull(httpMessageBuilder.getMessage().getHeaders().get(MessageHeaders.ID)); - Assert.assertNotNull(httpMessageBuilder.getMessage().getHeaders().get(MessageHeaders.TIMESTAMP)); - Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_REQUEST_METHOD), HttpMethod.GET.name()); - Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().get(EndpointUriResolver.REQUEST_PATH_HEADER_NAME), "/pet/${petId}"); - Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_REQUEST_URI), "/pet/${petId}"); - Assert.assertNull(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_QUERY_PARAMS)); - Assert.assertNull(httpMessageBuilder.getMessage().getHeaders().get(EndpointUriResolver.ENDPOINT_URI_HEADER_NAME)); - Assert.assertEquals(sendMessageAction.getEndpointUri(), "httpClient"); + var sendMessageAction = (SendMessageAction) result.getTestAction(actionIndex++); + assertThat(sendMessageAction.isForkMode()).isFalse(); + assertThat(sendMessageAction.getEndpointUri()).isEqualTo("httpClient"); + + String messageType = sendMessageAction.getMessageType(); + assertThat(sendMessageAction.getMessageBuilder()) + .isNotNull() + .isInstanceOf(HttpMessageBuilder.class) + .extracting(HttpMessageBuilder.class::cast) + .satisfies(httpMessageBuilder -> { + assertThat(httpMessageBuilder.buildMessagePayload(context, messageType)).isEqualTo(""); + assertThat(httpMessageBuilder.getMessage().getHeaders()) + .hasSize(5) + .hasEntrySatisfying(ID, e -> assertThat(e).isNotNull()) + .hasEntrySatisfying(TIMESTAMP, e -> assertThat(e).isNotNull()) + .doesNotContainKey(HTTP_QUERY_PARAMS) + .doesNotContainKey(ENDPOINT_URI_HEADER_NAME) + .hasEntrySatisfying(HTTP_REQUEST_METHOD, e -> assertThat(e).isEqualTo(GET.name())) + .hasEntrySatisfying(REQUEST_PATH_HEADER_NAME, e -> assertThat(e).isEqualTo("/pet/${petId}")) + .hasEntrySatisfying(HTTP_REQUEST_URI, e -> assertThat(e).isEqualTo("/pet/${petId}")); + }); Message controlMessage = new DefaultMessage(""); Message request = inboundQueue.receive(); headerValidator.validateMessage(request, controlMessage, context, new HeaderValidationContext()); validator.validateMessage(request, controlMessage, context, new DefaultValidationContext()); - ReceiveMessageAction receiveMessageAction = (ReceiveMessageAction) result.getTestAction(actionIndex++); - Assert.assertEquals(receiveMessageAction.getValidationContexts().size(), 3); - Assert.assertEquals(receiveMessageAction.getReceiveTimeout(), 0L); - Assert.assertTrue(receiveMessageAction.getValidationContexts().get(0) instanceof XmlMessageValidationContext); - Assert.assertTrue(receiveMessageAction.getValidationContexts().get(1) instanceof JsonMessageValidationContext); - Assert.assertTrue(receiveMessageAction.getValidationContexts().get(2) instanceof HeaderValidationContext); - - httpMessageBuilder = ((HttpMessageBuilder)receiveMessageAction.getMessageBuilder()); - Assert.assertNotNull(httpMessageBuilder); - - Assert.assertEquals(httpMessageBuilder.buildMessagePayload(context, receiveMessageAction.getMessageType()), - "{\"id\": \"@isNumber()@\",\"category\": {\"id\": \"@isNumber()@\",\"name\": \"@notEmpty()@\"},\"name\": \"@notEmpty()@\",\"photoUrls\": \"@ignore@\",\"tags\": \"@ignore@\",\"status\": \"@matches(available|pending|sold)@\"}"); - Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().size(), 5L); - Assert.assertNotNull(httpMessageBuilder.getMessage().getHeaders().get(MessageHeaders.ID)); - Assert.assertNotNull(httpMessageBuilder.getMessage().getHeaders().get(MessageHeaders.TIMESTAMP)); - Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_STATUS_CODE), 200); - Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_REASON_PHRASE), "OK"); - 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.getControlMessageProcessors().size(), 0); + var receiveMessageAction = (ReceiveMessageAction) result.getTestAction(actionIndex++); + assertThat(receiveMessageAction.getValidationContexts()).hasSize(3); + assertThat(receiveMessageAction.getReceiveTimeout()).isEqualTo(0L); + assertThat(receiveMessageAction.getValidationContexts().get(0)).isInstanceOf(XmlMessageValidationContext.class); + assertThat(receiveMessageAction.getValidationContexts().get(1)).isInstanceOf(JsonMessageValidationContext.class); + assertThat(receiveMessageAction.getValidationContexts().get(2)).isInstanceOf(HeaderValidationContext.class); + + var httpMessageBuilder = ((HttpMessageBuilder) receiveMessageAction.getMessageBuilder()); + assertThat(httpMessageBuilder).isNotNull(); + + assertThat(httpMessageBuilder.buildMessagePayload(context, receiveMessageAction.getMessageType())).isEqualTo("{\"id\": \"@isNumber()@\",\"category\": {\"id\": \"@isNumber()@\",\"name\": \"@notEmpty()@\"},\"name\": \"@notEmpty()@\",\"photoUrls\": \"@ignore@\",\"tags\": \"@ignore@\",\"status\": \"@matches(available|pending|sold)@\"}"); + assertThat(httpMessageBuilder.getMessage().getHeaders()).hasSize(5); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(ID)).isNotNull(); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(TIMESTAMP)).isNotNull(); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_STATUS_CODE)).isEqualTo(200); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_REASON_PHRASE)).isEqualTo("OK"); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_CONTENT_TYPE)).isEqualTo("application/json"); + assertThat(receiveMessageAction.getEndpoint()).isNull(); + assertThat(receiveMessageAction.getEndpointUri()).isEqualTo("httpClient"); + assertThat(receiveMessageAction.getMessageProcessors()).isEmpty(); + assertThat(receiveMessageAction.getControlMessageProcessors()).isEmpty(); sendMessageAction = (SendMessageAction) result.getTestAction(actionIndex++); - Assert.assertFalse(sendMessageAction.isForkMode()); - httpMessageBuilder = ((HttpMessageBuilder)sendMessageAction.getMessageBuilder()); - Assert.assertNotNull(httpMessageBuilder); - Assert.assertTrue(httpMessageBuilder.buildMessagePayload(context, sendMessageAction.getMessageType()).toString().startsWith("{\"id\": ")); - Assert.assertNotNull(httpMessageBuilder.getMessage().getHeaders().get(MessageHeaders.ID)); - Assert.assertNotNull(httpMessageBuilder.getMessage().getHeaders().get(MessageHeaders.TIMESTAMP)); + assertThat(sendMessageAction.isForkMode()).isFalse(); + httpMessageBuilder = ((HttpMessageBuilder) sendMessageAction.getMessageBuilder()); + assertThat(httpMessageBuilder).isNotNull(); + assertThat(httpMessageBuilder.buildMessagePayload(context, messageType).toString().startsWith("{\"id\": ")).isTrue(); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(ID)).isNotNull(); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(TIMESTAMP)).isNotNull(); Map requestHeaders = httpMessageBuilder.buildMessageHeaders(context); - Assert.assertEquals(requestHeaders.size(), 4L); - Assert.assertEquals(requestHeaders.get(HttpMessageHeaders.HTTP_REQUEST_METHOD), HttpMethod.POST.name()); - Assert.assertEquals(requestHeaders.get(EndpointUriResolver.REQUEST_PATH_HEADER_NAME), "/pet"); - Assert.assertEquals(requestHeaders.get(HttpMessageHeaders.HTTP_REQUEST_URI), "/pet"); - Assert.assertEquals(requestHeaders.get(HttpMessageHeaders.HTTP_CONTENT_TYPE), "application/json"); - Assert.assertNull(sendMessageAction.getEndpoint()); - Assert.assertEquals(sendMessageAction.getEndpointUri(), "httpClient"); + assertThat(requestHeaders).hasSize(4); + assertThat(requestHeaders.get(HTTP_REQUEST_METHOD)).isEqualTo(HttpMethod.POST.name()); + assertThat(requestHeaders.get(REQUEST_PATH_HEADER_NAME)).isEqualTo("/pet"); + assertThat(requestHeaders.get(HTTP_REQUEST_URI)).isEqualTo("/pet"); + assertThat(requestHeaders.get(HttpMessageHeaders.HTTP_CONTENT_TYPE)).isEqualTo("application/json"); + assertThat(sendMessageAction.getEndpoint()).isNull(); + assertThat(sendMessageAction.getEndpointUri()).isEqualTo("httpClient"); receiveMessageAction = (ReceiveMessageAction) result.getTestAction(actionIndex); - Assert.assertEquals(receiveMessageAction.getValidationContexts().size(), 3); - Assert.assertTrue(receiveMessageAction.getValidationContexts().get(0) instanceof XmlMessageValidationContext); - Assert.assertTrue(receiveMessageAction.getValidationContexts().get(1) instanceof JsonMessageValidationContext); - Assert.assertTrue(receiveMessageAction.getValidationContexts().get(2) instanceof HeaderValidationContext); - - httpMessageBuilder = ((HttpMessageBuilder)receiveMessageAction.getMessageBuilder()); - Assert.assertNotNull(httpMessageBuilder); - Assert.assertEquals(httpMessageBuilder.buildMessagePayload(context, receiveMessageAction.getMessageType()), ""); - Assert.assertNotNull(httpMessageBuilder.getMessage().getHeaders().get(MessageHeaders.ID)); - Assert.assertNotNull(httpMessageBuilder.getMessage().getHeaders().get(MessageHeaders.TIMESTAMP)); + assertThat(receiveMessageAction.getValidationContexts()).hasSize(3); + assertThat(receiveMessageAction.getValidationContexts().get(0)).isInstanceOf(XmlMessageValidationContext.class); + assertThat(receiveMessageAction.getValidationContexts().get(1)).isInstanceOf(JsonMessageValidationContext.class); + assertThat(receiveMessageAction.getValidationContexts().get(2)).isInstanceOf(HeaderValidationContext.class); + + httpMessageBuilder = ((HttpMessageBuilder) receiveMessageAction.getMessageBuilder()); + assertThat(httpMessageBuilder).isNotNull(); + assertThat(httpMessageBuilder.buildMessagePayload(context, receiveMessageAction.getMessageType())).isEqualTo(""); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(ID)).isNotNull(); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(TIMESTAMP)).isNotNull(); Map responseHeaders = httpMessageBuilder.buildMessageHeaders(context); - Assert.assertEquals(responseHeaders.size(), 2L); - Assert.assertEquals(responseHeaders.get(HttpMessageHeaders.HTTP_STATUS_CODE), 201); - Assert.assertEquals(responseHeaders.get(HttpMessageHeaders.HTTP_REASON_PHRASE), "CREATED"); - Assert.assertNull(receiveMessageAction.getEndpoint()); - Assert.assertEquals(receiveMessageAction.getEndpointUri(), "httpClient"); + assertThat(responseHeaders).hasSize(2); + assertThat(responseHeaders.get(HttpMessageHeaders.HTTP_STATUS_CODE)).isEqualTo(201); + assertThat(responseHeaders.get(HttpMessageHeaders.HTTP_REASON_PHRASE)).isEqualTo("CREATED"); + assertThat(receiveMessageAction.getEndpoint()).isNull(); + assertThat(receiveMessageAction.getEndpointUri()).isEqualTo("httpClient"); - Assert.assertEquals(receiveMessageAction.getVariableExtractors().size(), 0L); + assertThat(receiveMessageAction.getVariableExtractors()).isEmpty(); } @Test public void shouldLookupTestActionBuilder() { - Assert.assertTrue(YamlTestActionBuilder.lookup("openapi").isPresent()); - Assert.assertEquals(YamlTestActionBuilder.lookup("openapi").get().getClass(), OpenApi.class); + assertThat(YamlTestActionBuilder.lookup("openapi").isPresent()).isTrue(); + assertThat(YamlTestActionBuilder.lookup("openapi").get()).isOfAnyClassIn(OpenApi.class); } } diff --git a/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/petstore-v3.json b/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/petstore-v3.json index 618854948f..a3b60caff8 100644 --- a/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/petstore-v3.json +++ b/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/petstore-v3.json @@ -111,6 +111,15 @@ }, "in": "query", "required": false + }, + { + "name": "correlationIds", + "description": "Output details", + "schema": { + "type": "string" + }, + "in": "header", + "required": false } ], "responses": { diff --git a/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/petstore-v3.yaml b/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/petstore-v3.yaml index bf3bde73d9..5d3e874393 100644 --- a/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/petstore-v3.yaml +++ b/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/petstore-v3.yaml @@ -76,6 +76,13 @@ paths: type: boolean in: query required: false + - + name: correlationIds + description: ID to trace a request + schema: + type: string + in: header + required: false responses: '200': content: diff --git a/pom.xml b/pom.xml index 981380096e..f60de64b62 100644 --- a/pom.xml +++ b/pom.xml @@ -273,8 +273,8 @@ 1.4.20 3.9.2 - false - false + ${skipTests} + ${skipTests} false diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java index 42f707e3f4..6ae9023ad4 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java @@ -8,6 +8,9 @@ import org.citrusframework.openapi.actions.OpenApiClientRequestActionBuilder; import org.citrusframework.openapi.actions.OpenApiClientResponseActionBuilder; +import java.util.HashMap; +import java.util.Map; + import static org.citrusframework.spi.Resources.create; import static org.springframework.http.HttpStatus.OK; @@ -17,48 +20,66 @@ public class OpenapiPetstore { create("src/test/resources/apis/petstore.yaml") ); - public static OpenapiPetstoreBuilder openapiPetstore(HttpClient httpClient, TestCaseRunner runner) { - return new OpenapiPetstoreBuilder(httpClient, runner); + public static OpenapiPetstoreBuilder openapiPetstore(HttpClient httpClient) { + return new OpenapiPetstoreBuilder(httpClient); } public static class OpenapiPetstoreBuilder { private final HttpClient httpClient; - private final TestCaseRunner runner; - OpenapiPetstoreBuilder(HttpClient httpClient, TestCaseRunner runner) { + OpenapiPetstoreBuilder(HttpClient httpClient) { this.httpClient = httpClient; - // TODO the runner is only needed to set the request params as test-variables, which is ugly... - // see comment in `withPetId()` - this.runner = runner; } public GetPetByIdBuilder getPetById() { - return new GetPetByIdBuilder(httpClient, petstoreSpec, runner); + return new GetPetByIdBuilder(httpClient, petstoreSpec); } public static class GetPetByIdBuilder extends OpenApiClientActionBuilder { public static final String OPERATION_ID = "getPetById"; - private final TestCaseRunner runner; + private final Map variables = new HashMap<>(); - public GetPetByIdBuilder(Endpoint httpClient, OpenApiSpecification specification, TestCaseRunner runner) { + public GetPetByIdBuilder(Endpoint httpClient, OpenApiSpecification specification) { super(httpClient, specification); - this.runner = runner; } public GetPetByIdBuilder withPetId(String petId) { - // TODO? move that to super and make it more explicit to set params (e.g. not implicitly via testcase-variables) - runner.variable("petId", petId); + variables.put("petId", petId); + return this; + } + + public GetPetByIdBuilder withCorrelationIds(String correlationIds) { + variables.put("correlationIds", correlationIds); + return this; + } + + public GetPetByIdBuilder withVerbose(boolean verbose) { + variables.put("verbose", verbose); return this; } public OpenApiClientRequestActionBuilder send() { - return send(OPERATION_ID); + var openApiOperation = OpenApiOperationBuilder.operation(OPERATION_ID).withParameters(variables); + var send = send(openApiOperation); + send.fork(true); + return send; } - public OpenApiClientResponseActionBuilder receive() { + public OpenApiClientResponseActionBuilder receive(TestCaseRunner runner) { return receive(OPERATION_ID, OK); } + + public static class GetPetByIdMessageBuilder extends OpenApiClientRequestActionBuilder { + + protected GetPetByIdMessageBuilder(OpenApiClientRequestActionBuilder.OpenApiClientRequestMessageBuilder messageBuilder) { + super(messageBuilder); + } + + public GetPetByIdMessageBuilder create() { + return null; + } + } } } } diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java index 1ce15dd6de..c57ed41f3b 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java @@ -56,18 +56,19 @@ public void beforeTest() { @Test @CitrusTest void testFluentGeneratedOpenapiAction(@CitrusResource TestCaseRunner runner) { - runner.$(openapiPetstore(httpClient, runner) + runner.$(openapiPetstore(httpClient) .getPetById() - .withPetId("1001") + .withPetId("2002") + .withCorrelationIds("5599") + .withVerbose(true) .send() - // TODO? set `.fork(true)` in the `.send()` - .fork(true)); + ); respondPet(runner); - runner.$(openapiPetstore(httpClient, runner) + runner.$(openapiPetstore(httpClient) .getPetById() - .receive() + .receive(runner) .message() .type(JSON) .contentType("application/json") @@ -77,8 +78,10 @@ void testFluentGeneratedOpenapiAction(@CitrusResource TestCaseRunner runner) { private void respondPet(TestCaseRunner runner) { runner.$(http().server(httpServer) .receive() - .get("/pet/1001") + .get("/pet/2002") .message() + .queryParam("verbose", "true") + .header("correlationIds", "5599") .accept("@contains('application/json')@")); runner.$(http().server(httpServer) .send() diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/petstore.yaml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/petstore.yaml index 4c834d1e3b..6ef6cf4658 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/petstore.yaml +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/petstore.yaml @@ -168,6 +168,20 @@ paths: schema: type: integer format: int64 + - + name: verbose + description: Output details + schema: + type: boolean + in: query + required: false + - + name: correlationIds + description: ID to trace a request + schema: + type: string + in: header + required: false responses: '200': description: successful operation