From 85cfcff097418de8b84005a6bfa0618a0208f8cc Mon Sep 17 00:00:00 2001 From: Joel Rosario Date: Tue, 25 Jun 2024 20:08:27 +0530 Subject: [PATCH 1/2] Optionality of xml attributes is now honored, and a bug in resolution of XML nodes referring to other XML nodes where the referred node's attributes were dropped is fixed --- .../conversions/OpenApiSpecification.kt | 7 +- .../in/specmatic/core/pattern/XMLPattern.kt | 5 +- .../conversions/OpenApiSpecificationTest.kt | 117 ++++++++++++++++++ 3 files changed, 127 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/in/specmatic/conversions/OpenApiSpecification.kt b/core/src/main/kotlin/in/specmatic/conversions/OpenApiSpecification.kt index a064a3610..9013f9ea6 100644 --- a/core/src/main/kotlin/in/specmatic/conversions/OpenApiSpecification.kt +++ b/core/src/main/kotlin/in/specmatic/conversions/OpenApiSpecification.kt @@ -1142,7 +1142,12 @@ class OpenApiSpecification( } val attributes: Map = attributeProperties.map { (name, schema) -> - name to toSpecmaticPattern(schema, emptyList()) + val attributeName = if(name !in schema.required.orEmpty()) + "$name.opt" + else + name + + attributeName to toSpecmaticPattern(schema, emptyList()) }.toMap() name ?: throw ContractException("Could not determine name for an xml node") diff --git a/core/src/main/kotlin/in/specmatic/core/pattern/XMLPattern.kt b/core/src/main/kotlin/in/specmatic/core/pattern/XMLPattern.kt index f25a8b5fd..a8da88173 100644 --- a/core/src/main/kotlin/in/specmatic/core/pattern/XMLPattern.kt +++ b/core/src/main/kotlin/in/specmatic/core/pattern/XMLPattern.kt @@ -110,7 +110,10 @@ data class XMLPattern(override val pattern: XMLTypeData = XMLTypeData(realName = val matchingType = if (this.pattern.attributes.containsKey(TYPE_ATTRIBUTE_NAME)) { val typeName = this.pattern.getAttributeValue(TYPE_ATTRIBUTE_NAME) val xmlType = (resolver.getPattern("($typeName)") as XMLPattern) - xmlType.copy(pattern = xmlType.pattern.copy(name = this.pattern.name, realName = this.pattern.realName, attributes = this.pattern.attributes.filterKeys { it != TYPE_ATTRIBUTE_NAME })) + val attributesFromReferring = this.pattern.attributes.filterKeys { it != TYPE_ATTRIBUTE_NAME } + val attributesFromReferred = xmlType.pattern.attributes.filterKeys { it != TYPE_ATTRIBUTE_NAME } + val attributes = attributesFromReferred + attributesFromReferring + xmlType.copy(pattern = xmlType.pattern.copy(name = this.pattern.name, realName = this.pattern.realName, attributes = attributes)) } else { this } diff --git a/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt b/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt index 688925cdd..feb868914 100644 --- a/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt +++ b/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt @@ -13,6 +13,7 @@ import `in`.specmatic.mock.ScenarioStub import `in`.specmatic.stub.HttpStub import `in`.specmatic.stub.HttpStubData import `in`.specmatic.stub.createStubFromContracts +import `in`.specmatic.stub.stringToMockScenario import `in`.specmatic.test.TestExecutor import `in`.specmatic.trimmedLinesString import io.ktor.util.reflect.* @@ -7772,6 +7773,122 @@ paths: assertThat(results.success()).withFailMessage(results.report()).isTrue() } + @Test + fun `referenecs in an XML structure which contains a ref should get dereferenced`() { + val xmlSpec = """ + openapi: 3.0.0 + info: + title: Testing API + version: 1.0.0 + description: | + Testing XML + paths: + /ReqListKeys: + post: + summary: Request list of keys + operationId: reqListKeys + requestBody: + required: true + content: + application/xml: + schema: + ${"$"}ref: '#/components/schemas/ReqListKeys' + responses: + '200': + description: Successful response + content: + application/xml: + schema: + ${"$"}ref: '#/components/schemas/RespListKeys' + components: + schemas: + ReqListKeys: + type: object + xml: + name: "ReqListKeys" + namespace: "http://xyz.org/upi/schema//" + prefix: "upi" + properties: + Head: + ${"$"}ref: '#/components/schemas/Head' + Head: + type: object + xml: + name: "Head" + properties: + msgId: + type: string + xml: + name: "msgId" + attribute: true + orgId: + type: string + xml: + name: "orgId" + attribute: true + prodType: + type: string + xml: + name: "prodType" + attribute: true + ts: + type: string + xml: + name: "ts" + attribute: true + ver: + type: string + enum: [1.0, 2.0] + xml: + name: "ver" + attribute: true + RespListKeys: + type: object + xml: + name: "RespListKeys" + namespace: "http://xyz.org/upi/schema/" + prefix: "ns2" + properties: + Head: + ${"$"}ref: '#/components/schemas/Head' + + """.trimIndent() + + val feature = OpenApiSpecification.fromYAML(xmlSpec, "").toFeature() + + val rawStub = """ + { + "http-request": { + "path": "/ReqListKeys", + "method": "POST", + "headers": { + "Content-Type": "application/xml" + }, + "body": "\n\n \n" + }, + "http-response": { + "status": 200, + "body": "\n\n \n\n", + "status-text": "OK", + "headers": { + "Content-Type": "application/xml" + } + } + } + """.trimIndent() + + val expectation: ScenarioStub = stringToMockScenario(StringValue(rawStub)) + + HttpStub(feature, listOf(expectation)).use { stub -> + val request = expectation.request + + val response = stub.client.execute(request) + + assertThat(response.status).isEqualTo(200) + assertThat(response.body.toStringLiteral()).contains("msgId") + } + } + private fun ignoreButLogException(function: () -> OpenApiSpecification) { try { function() From 6c81e8cd5a476484b01fe388172e071923992041 Mon Sep 17 00:00:00 2001 From: Joel Rosario Date: Tue, 25 Jun 2024 20:26:31 +0530 Subject: [PATCH 2/2] Typo fix --- .../kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt b/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt index feb868914..4c5fd7880 100644 --- a/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt +++ b/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt @@ -7774,7 +7774,7 @@ paths: } @Test - fun `referenecs in an XML structure which contains a ref should get dereferenced`() { + fun `references in an XML structure which contains a ref should get dereferenced`() { val xmlSpec = """ openapi: 3.0.0 info: