From f386bdecb06f56c6f051dfe6801847a0f847ca8a Mon Sep 17 00:00:00 2001 From: Sufiyan Date: Mon, 18 Nov 2024 13:17:47 +0530 Subject: [PATCH] Parse unreferenced or indirectly referenced schemas - Schemas used in an allOf or not referenced in operations should be parsed into patterns and made accessible when required. --- .../conversions/OpenApiSpecification.kt | 20 ++++++- .../conversions/OpenApiSpecificationTest.kt | 58 +++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/io/specmatic/conversions/OpenApiSpecification.kt b/core/src/main/kotlin/io/specmatic/conversions/OpenApiSpecification.kt index 22c56744e..b70b11ed3 100644 --- a/core/src/main/kotlin/io/specmatic/conversions/OpenApiSpecification.kt +++ b/core/src/main/kotlin/io/specmatic/conversions/OpenApiSpecification.kt @@ -223,18 +223,32 @@ class OpenApiSpecification( val name = File(openApiFilePath).name val (scenarioInfos, stubsFromExamples) = toScenarioInfos() + val unreferencedSchemaPatterns = parseUnreferencedSchemas() + val updatedScenarios = scenarioInfos.map { + Scenario(it).copy( + dictionary = dictionary, + attributeSelectionPattern = specmaticConfig.attributeSelectionPattern, + patterns = it.patterns + unreferencedSchemaPatterns + ) + } return Feature( - scenarioInfos.map { Scenario(it).copy(dictionary = dictionary, attributeSelectionPattern = specmaticConfig.attributeSelectionPattern) }, name = name, path = openApiFilePath, sourceProvider = sourceProvider, + updatedScenarios, name = name, path = openApiFilePath, sourceProvider = sourceProvider, sourceRepository = sourceRepository, sourceRepositoryBranch = sourceRepositoryBranch, specification = specificationPath, serviceType = SERVICE_TYPE_HTTP, stubsFromExamples = stubsFromExamples, - specmaticConfig = specmaticConfig + specmaticConfig = specmaticConfig, ) } + private fun parseUnreferencedSchemas(): Map { + return openApiSchemas().filterNot { withPatternDelimiters(it.key) in patterns }.map { + withPatternDelimiters(it.key) to toSpecmaticPattern(it.value, emptyList()) + }.toMap() + } + override fun toScenarioInfos(): Pair, Map>>> { val ( scenarioInfos: List, @@ -791,6 +805,8 @@ class OpenApiSpecification( private fun openApiPaths() = parsedOpenApi.paths.orEmpty() + private fun openApiSchemas() = parsedOpenApi.components?.schemas.orEmpty() + private fun isNumber(value: String): Boolean { return value.toIntOrNull() != null } diff --git a/core/src/test/kotlin/io/specmatic/conversions/OpenApiSpecificationTest.kt b/core/src/test/kotlin/io/specmatic/conversions/OpenApiSpecificationTest.kt index 014157c38..6896ed630 100644 --- a/core/src/test/kotlin/io/specmatic/conversions/OpenApiSpecificationTest.kt +++ b/core/src/test/kotlin/io/specmatic/conversions/OpenApiSpecificationTest.kt @@ -9560,6 +9560,64 @@ paths: } } + @Test + fun `should include unreferenced or indirectly referenced schema patterns`() { + val specContent = """ + openapi: 3.0.3 + info: + title: Products API + version: 1.0.0 + paths: + /products: + get: + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: array + items: + ${'$'}ref: '#/components/schemas/ExtendedDetails' + components: + schemas: + User: + type: object + properties: + name: + type: string + ExtendedDetails: + allOf: + - ${'$'}ref: '#/components/schemas/BaseDetails' + - ${'$'}ref: '#/components/schemas/User' + BaseDetails: + type: object + properties: + id: + type: string + email: + type: string + required: + - id + Address: + type: object + properties: + street: + type: string + city: + type: string + """.trimIndent() + val feature = OpenApiSpecification.fromYAML(specContent, "").toFeature() + val directlyNonReferencedPatterns = listOf("(BaseDetails)", "(User)", "(Address)") + + assertThat(feature.scenarios).allSatisfy { scenario -> + directlyNonReferencedPatterns.forEach { + assertThat(scenario.patterns).containsKey(it) + assertThat(scenario.resolver.newPatterns).containsKey(it) + } + } + } + private fun ignoreButLogException(function: () -> OpenApiSpecification) { try { function()