From aa7c499fbaa267c4648197926df6e0d269611e80 Mon Sep 17 00:00:00 2001 From: Sukesh Seth Date: Fri, 31 Jan 2025 18:53:22 +0530 Subject: [PATCH 1/6] Make test field of the config private --- .../main/kotlin/io/specmatic/core/Feature.kt | 6 ++- .../io/specmatic/core/SpecmaticConfig.kt | 50 +++++++++++++++---- .../core/config/v2/SpecmaticConfigV2.kt | 3 +- .../specmatic/core/SpecmaticConfigKtTest.kt | 6 +-- .../specmatic/test/SpecmaticJUnitSupport.kt | 2 +- 5 files changed, 50 insertions(+), 17 deletions(-) diff --git a/core/src/main/kotlin/io/specmatic/core/Feature.kt b/core/src/main/kotlin/io/specmatic/core/Feature.kt index 5177c8c6f..a8d2caf67 100644 --- a/core/src/main/kotlin/io/specmatic/core/Feature.kt +++ b/core/src/main/kotlin/io/specmatic/core/Feature.kt @@ -16,6 +16,7 @@ import io.cucumber.messages.IdGenerator.Incrementing import io.cucumber.messages.types.* import io.cucumber.messages.types.Examples import io.ktor.http.* +import io.specmatic.core.SpecmaticConfig.Companion.getTestConfiguration import io.specmatic.core.discriminator.DiscriminatorBasedItem import io.specmatic.core.discriminator.DiscriminatorMetadata import io.specmatic.core.utilities.* @@ -128,9 +129,10 @@ data class Feature( val strictMode: Boolean = false ): IFeature { fun enableGenerativeTesting(onlyPositive: Boolean = false): Feature { + val testConfiguration = getTestConfiguration(specmaticConfig) val updatedSpecmaticConfig = specmaticConfig.copy( - test = specmaticConfig.test?.copy( - resiliencyTests = specmaticConfig.test.resiliencyTests?.copy( + test = testConfiguration?.copy( + resiliencyTests = testConfiguration.getResiliencyTests()?.copy( enable = if(onlyPositive) ResiliencyTestSuite.positiveOnly else ResiliencyTestSuite.all ) ) diff --git a/core/src/main/kotlin/io/specmatic/core/SpecmaticConfig.kt b/core/src/main/kotlin/io/specmatic/core/SpecmaticConfig.kt index 7efeafa0b..4389e2491 100644 --- a/core/src/main/kotlin/io/specmatic/core/SpecmaticConfig.kt +++ b/core/src/main/kotlin/io/specmatic/core/SpecmaticConfig.kt @@ -118,7 +118,7 @@ data class SpecmaticConfig( private val repository: RepositoryInfo? = null, val report: ReportConfiguration? = null, val security: SecurityConfiguration? = null, - val test: TestConfiguration? = TestConfiguration(), + private val test: TestConfiguration? = TestConfiguration(), val stub: StubConfiguration = StubConfiguration(), val virtualService: VirtualServiceConfiguration = VirtualServiceConfiguration(), private val examples: List? = null, @@ -140,6 +140,11 @@ data class SpecmaticConfig( fun getPipeline(specmaticConfig: SpecmaticConfig): Pipeline? { return specmaticConfig.pipeline } + + @JsonIgnore + fun getTestConfiguration(specmaticConfig: SpecmaticConfig): TestConfiguration? { + return specmaticConfig.test + } } @JsonIgnore @@ -149,7 +154,7 @@ data class SpecmaticConfig( @JsonIgnore fun isExtensibleSchemaEnabled(): Boolean { - return test?.allowExtensibleSchema ?: getBooleanValue(EXTENSIBLE_SCHEMA) + return test?.getAllowExtensibleSchema() ?: getBooleanValue(EXTENSIBLE_SCHEMA) } @JsonIgnore @@ -164,7 +169,7 @@ data class SpecmaticConfig( @JsonIgnore fun isResponseValueValidationEnabled(): Boolean { - return test?.validateResponseValues ?: getBooleanValue(VALIDATE_RESPONSE_VALUE) + return test?.getValidateResponseValues() ?: getBooleanValue(VALIDATE_RESPONSE_VALUE) } @JsonIgnore @@ -179,7 +184,12 @@ data class SpecmaticConfig( @JsonIgnore fun getResiliencyTestsEnable(): ResiliencyTestSuite { - return test?.resiliencyTests?.enable ?: ResiliencyTestSuite.none + return test?.getResiliencyTests()?.getEnableTestSuite() ?: ResiliencyTestSuite.none + } + + @JsonIgnore + fun getTestTimeoutInMilliseconds(): Long? { + return test?.getTimeoutInMilliseconds() } @JsonIgnore @@ -268,26 +278,46 @@ data class SpecmaticConfig( } data class TestConfiguration( - val resiliencyTests: ResiliencyTestsConfig? = ResiliencyTestsConfig( + private val resiliencyTests: ResiliencyTestsConfig? = ResiliencyTestsConfig( isResiliencyTestFlagEnabled = getBooleanValue(SPECMATIC_GENERATIVE_TESTS), isOnlyPositiveFlagEnabled = getBooleanValue(ONLY_POSITIVE) ), - val validateResponseValues: Boolean? = null, - val allowExtensibleSchema: Boolean? = null, - val timeoutInMilliseconds: Long? = getLongValue(SPECMATIC_TEST_TIMEOUT) -) + private val validateResponseValues: Boolean? = null, + private val allowExtensibleSchema: Boolean? = null, + private val timeoutInMilliseconds: Long? = getLongValue(SPECMATIC_TEST_TIMEOUT) +) { + fun getResiliencyTests(): ResiliencyTestsConfig? { + return resiliencyTests + } + + fun getValidateResponseValues(): Boolean? { + return validateResponseValues + } + + fun getAllowExtensibleSchema(): Boolean? { + return allowExtensibleSchema + } + + fun getTimeoutInMilliseconds(): Long? { + return timeoutInMilliseconds + } +} enum class ResiliencyTestSuite { all, positiveOnly, none } data class ResiliencyTestsConfig( - val enable: ResiliencyTestSuite? = null + private val enable: ResiliencyTestSuite? = null ) { constructor(isResiliencyTestFlagEnabled: Boolean, isOnlyPositiveFlagEnabled: Boolean) : this( enable = getEnableFrom(isResiliencyTestFlagEnabled, isOnlyPositiveFlagEnabled) ) + fun getEnableTestSuite(): ResiliencyTestSuite? { + return enable + } + companion object { private fun getEnableFrom( isResiliencyTestFlagEnabled: Boolean, diff --git a/core/src/main/kotlin/io/specmatic/core/config/v2/SpecmaticConfigV2.kt b/core/src/main/kotlin/io/specmatic/core/config/v2/SpecmaticConfigV2.kt index c441e7b34..916705589 100644 --- a/core/src/main/kotlin/io/specmatic/core/config/v2/SpecmaticConfigV2.kt +++ b/core/src/main/kotlin/io/specmatic/core/config/v2/SpecmaticConfigV2.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonAlias import io.specmatic.core.* import io.specmatic.core.SpecmaticConfig.Companion.getPipeline import io.specmatic.core.SpecmaticConfig.Companion.getRepository +import io.specmatic.core.SpecmaticConfig.Companion.getTestConfiguration import io.specmatic.core.config.SpecmaticConfigVersion import io.specmatic.core.config.SpecmaticVersionedConfig import io.specmatic.core.config.SpecmaticVersionedConfigLoader @@ -71,7 +72,7 @@ data class SpecmaticConfigV2( repository = getRepository(config), report = config.report, security = config.security, - test = config.test, + test = getTestConfiguration(config), stub = config.stub, virtualService = config.virtualService, examples = config.getExamples(), diff --git a/core/src/test/kotlin/io/specmatic/core/SpecmaticConfigKtTest.kt b/core/src/test/kotlin/io/specmatic/core/SpecmaticConfigKtTest.kt index aa8337968..3c3af3fdb 100644 --- a/core/src/test/kotlin/io/specmatic/core/SpecmaticConfigKtTest.kt +++ b/core/src/test/kotlin/io/specmatic/core/SpecmaticConfigKtTest.kt @@ -77,7 +77,7 @@ internal class SpecmaticConfigKtTest { assertThat(htmlConfig?.heading).isEqualTo("Test Results") assertThat(htmlConfig?.outputDirectory).isEqualTo("output") - assertThat(config.test?.timeoutInMilliseconds).isEqualTo(3000) + assertThat(config.getTestTimeoutInMilliseconds()).isEqualTo(3000) } @Test @@ -171,7 +171,7 @@ internal class SpecmaticConfigKtTest { assertThat(config.isExtensibleSchemaEnabled()).isFalse() assertThat(config.getExamples()).isEqualTo(listOf("folder1/examples", "folder2/examples")) assertThat(config.stub.delayInMilliseconds).isEqualTo(1000L) - assertThat(config.test?.timeoutInMilliseconds).isEqualTo(5000) + assertThat(config.getTestTimeoutInMilliseconds()).isEqualTo(5000) } finally { properties.forEach { System.clearProperty(it.key) } } @@ -220,7 +220,7 @@ internal class SpecmaticConfigKtTest { assertThat(config.isExtensibleSchemaEnabled()).isTrue() assertThat(config.getExamples()).isEqualTo(listOf("folder1/examples", "folder2/examples")) assertThat(config.stub.delayInMilliseconds).isEqualTo(1000L) - assertThat(config.test?.timeoutInMilliseconds).isEqualTo(3000) + assertThat(config.getTestTimeoutInMilliseconds()).isEqualTo(3000) } finally { props.forEach { System.clearProperty(it.key) } } diff --git a/junit5-support/src/main/kotlin/io/specmatic/test/SpecmaticJUnitSupport.kt b/junit5-support/src/main/kotlin/io/specmatic/test/SpecmaticJUnitSupport.kt index c09c029dc..5caba1053 100644 --- a/junit5-support/src/main/kotlin/io/specmatic/test/SpecmaticJUnitSupport.kt +++ b/junit5-support/src/main/kotlin/io/specmatic/test/SpecmaticJUnitSupport.kt @@ -241,7 +241,7 @@ open class SpecmaticJUnitSupport { specmaticConfig = getSpecmaticConfig() - val timeoutInMilliseconds = specmaticConfig?.test?.timeoutInMilliseconds ?: try { + val timeoutInMilliseconds = specmaticConfig?.getTestTimeoutInMilliseconds() ?: try { getLongValue(SPECMATIC_TEST_TIMEOUT) } catch (e: NumberFormatException) { throw ContractException("$SPECMATIC_TEST_TIMEOUT should be a value of type long") From 96511a8937c1601da03258e9623dac4603847dc9 Mon Sep 17 00:00:00 2001 From: Sukesh Seth Date: Mon, 3 Feb 2025 13:49:03 +0530 Subject: [PATCH 2/6] Move default values of TestConfiguration to the getters --- .../kotlin/io/specmatic/core/SpecmaticConfig.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core/src/main/kotlin/io/specmatic/core/SpecmaticConfig.kt b/core/src/main/kotlin/io/specmatic/core/SpecmaticConfig.kt index c61a70070..1849fc263 100644 --- a/core/src/main/kotlin/io/specmatic/core/SpecmaticConfig.kt +++ b/core/src/main/kotlin/io/specmatic/core/SpecmaticConfig.kt @@ -288,16 +288,16 @@ data class SpecmaticConfig( } data class TestConfiguration( - private val resiliencyTests: ResiliencyTestsConfig? = ResiliencyTestsConfig( - isResiliencyTestFlagEnabled = getBooleanValue(SPECMATIC_GENERATIVE_TESTS), - isOnlyPositiveFlagEnabled = getBooleanValue(ONLY_POSITIVE) - ), + private val resiliencyTests: ResiliencyTestsConfig? = null, private val validateResponseValues: Boolean? = null, private val allowExtensibleSchema: Boolean? = null, - private val timeoutInMilliseconds: Long? = getLongValue(SPECMATIC_TEST_TIMEOUT) + private val timeoutInMilliseconds: Long? = null ) { - fun getResiliencyTests(): ResiliencyTestsConfig? { - return resiliencyTests + fun getResiliencyTests(): ResiliencyTestsConfig { + return resiliencyTests ?: ResiliencyTestsConfig( + isResiliencyTestFlagEnabled = getBooleanValue(SPECMATIC_GENERATIVE_TESTS), + isOnlyPositiveFlagEnabled = getBooleanValue(ONLY_POSITIVE) + ) } fun getValidateResponseValues(): Boolean? { @@ -309,7 +309,7 @@ data class TestConfiguration( } fun getTimeoutInMilliseconds(): Long? { - return timeoutInMilliseconds + return timeoutInMilliseconds ?: getLongValue(SPECMATIC_TEST_TIMEOUT) } } From 5c0cd6b895e3ad654a0a6febd5f38d2c6556cb20 Mon Sep 17 00:00:00 2001 From: Sukesh Seth Date: Mon, 3 Feb 2025 14:06:03 +0530 Subject: [PATCH 3/6] Move the copying logic for the testConfiguration from Feature to SpecmaticConfig --- core/src/main/kotlin/io/specmatic/core/Feature.kt | 10 +--------- .../main/kotlin/io/specmatic/core/SpecmaticConfig.kt | 11 +++++++++++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/core/src/main/kotlin/io/specmatic/core/Feature.kt b/core/src/main/kotlin/io/specmatic/core/Feature.kt index a8d2caf67..f52483ec7 100644 --- a/core/src/main/kotlin/io/specmatic/core/Feature.kt +++ b/core/src/main/kotlin/io/specmatic/core/Feature.kt @@ -16,7 +16,6 @@ import io.cucumber.messages.IdGenerator.Incrementing import io.cucumber.messages.types.* import io.cucumber.messages.types.Examples import io.ktor.http.* -import io.specmatic.core.SpecmaticConfig.Companion.getTestConfiguration import io.specmatic.core.discriminator.DiscriminatorBasedItem import io.specmatic.core.discriminator.DiscriminatorMetadata import io.specmatic.core.utilities.* @@ -129,14 +128,7 @@ data class Feature( val strictMode: Boolean = false ): IFeature { fun enableGenerativeTesting(onlyPositive: Boolean = false): Feature { - val testConfiguration = getTestConfiguration(specmaticConfig) - val updatedSpecmaticConfig = specmaticConfig.copy( - test = testConfiguration?.copy( - resiliencyTests = testConfiguration.getResiliencyTests()?.copy( - enable = if(onlyPositive) ResiliencyTestSuite.positiveOnly else ResiliencyTestSuite.all - ) - ) - ) + val updatedSpecmaticConfig = specmaticConfig.copyResiliencyTestsConfig(onlyPositive) return this.copy(flagsBased = this.flagsBased.copy( generation = GenerativeTestsEnabled(onlyPositive), diff --git a/core/src/main/kotlin/io/specmatic/core/SpecmaticConfig.kt b/core/src/main/kotlin/io/specmatic/core/SpecmaticConfig.kt index 1849fc263..20f4875b9 100644 --- a/core/src/main/kotlin/io/specmatic/core/SpecmaticConfig.kt +++ b/core/src/main/kotlin/io/specmatic/core/SpecmaticConfig.kt @@ -197,6 +197,17 @@ data class SpecmaticConfig( return test?.getTimeoutInMilliseconds() } + @JsonIgnore + fun copyResiliencyTestsConfig(onlyPositive: Boolean): SpecmaticConfig { + return this.copy( + test = test?.copy( + resiliencyTests = test.getResiliencyTests().copy( + enable = if (onlyPositive) ResiliencyTestSuite.positiveOnly else ResiliencyTestSuite.all + ) + ) + ) + } + @JsonIgnore fun getStubGenerative(): Boolean { return stub.generative ?: false From 68bb1cee4369c6584e52b1876318d08c5c402e02 Mon Sep 17 00:00:00 2001 From: Sukesh Seth Date: Mon, 3 Feb 2025 14:12:49 +0530 Subject: [PATCH 4/6] Move config copy inline --- core/src/main/kotlin/io/specmatic/core/Feature.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/main/kotlin/io/specmatic/core/Feature.kt b/core/src/main/kotlin/io/specmatic/core/Feature.kt index f52483ec7..4b9fb44ea 100644 --- a/core/src/main/kotlin/io/specmatic/core/Feature.kt +++ b/core/src/main/kotlin/io/specmatic/core/Feature.kt @@ -128,13 +128,11 @@ data class Feature( val strictMode: Boolean = false ): IFeature { fun enableGenerativeTesting(onlyPositive: Boolean = false): Feature { - val updatedSpecmaticConfig = specmaticConfig.copyResiliencyTestsConfig(onlyPositive) - return this.copy(flagsBased = this.flagsBased.copy( generation = GenerativeTestsEnabled(onlyPositive), positivePrefix = POSITIVE_TEST_DESCRIPTION_PREFIX, negativePrefix = NEGATIVE_TEST_DESCRIPTION_PREFIX), - specmaticConfig = updatedSpecmaticConfig + specmaticConfig = specmaticConfig.copyResiliencyTestsConfig(onlyPositive) ) } From 7d0b6fce3ae4e9f055a8b70b085caede4c5f4b69 Mon Sep 17 00:00:00 2001 From: Joel Rosario Date: Thu, 6 Feb 2025 19:39:30 +0530 Subject: [PATCH 5/6] Fetch the test configuration using helper method when converting between config versions --- .../kotlin/io/specmatic/core/config/v3/SpecmaticConfigV3.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/kotlin/io/specmatic/core/config/v3/SpecmaticConfigV3.kt b/core/src/main/kotlin/io/specmatic/core/config/v3/SpecmaticConfigV3.kt index cbc41d7ca..886da175a 100644 --- a/core/src/main/kotlin/io/specmatic/core/config/v3/SpecmaticConfigV3.kt +++ b/core/src/main/kotlin/io/specmatic/core/config/v3/SpecmaticConfigV3.kt @@ -72,7 +72,7 @@ data class SpecmaticConfigV3( repository = getRepository(config), report = config.report, security = getSecurityConfiguration(config), - test = config.test, + test = SpecmaticConfig.getTestConfiguration(config), stub = config.stub, virtualService = SpecmaticConfig.getVirtualServiceConfiguration(config), examples = config.getExamples(), From 820c6beeb75d97e970006d4f5b1e8feb1fb633f1 Mon Sep 17 00:00:00 2001 From: Joel Rosario Date: Thu, 6 Feb 2025 19:54:59 +0530 Subject: [PATCH 6/6] Added tests for the test configuration --- .../io/specmatic/core/SpecmaticConfig.kt | 6 +-- .../core/config/SpecmaticConfigAllTest.kt | 54 ++++++++++++++++--- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/core/src/main/kotlin/io/specmatic/core/SpecmaticConfig.kt b/core/src/main/kotlin/io/specmatic/core/SpecmaticConfig.kt index e245ddc29..92452ae93 100644 --- a/core/src/main/kotlin/io/specmatic/core/SpecmaticConfig.kt +++ b/core/src/main/kotlin/io/specmatic/core/SpecmaticConfig.kt @@ -230,12 +230,12 @@ data class SpecmaticConfig( @JsonIgnore fun isResiliencyTestingEnabled(): Boolean { - return (getResiliencyTestsEnable() != ResiliencyTestSuite.none) + return (getResiliencyTestsEnabled() != ResiliencyTestSuite.none) } @JsonIgnore fun isOnlyPositiveTestingEnabled(): Boolean { - return (getResiliencyTestsEnable() == ResiliencyTestSuite.positiveOnly) + return (getResiliencyTestsEnabled() == ResiliencyTestSuite.positiveOnly) } @JsonIgnore @@ -254,7 +254,7 @@ data class SpecmaticConfig( } @JsonIgnore - fun getResiliencyTestsEnable(): ResiliencyTestSuite { + fun getResiliencyTestsEnabled(): ResiliencyTestSuite { return test?.getResiliencyTests()?.getEnableTestSuite() ?: ResiliencyTestSuite.none } diff --git a/core/src/test/kotlin/io/specmatic/core/config/SpecmaticConfigAllTest.kt b/core/src/test/kotlin/io/specmatic/core/config/SpecmaticConfigAllTest.kt index 54a3d1528..24f0bbd1e 100644 --- a/core/src/test/kotlin/io/specmatic/core/config/SpecmaticConfigAllTest.kt +++ b/core/src/test/kotlin/io/specmatic/core/config/SpecmaticConfigAllTest.kt @@ -4,9 +4,7 @@ import com.fasterxml.jackson.databind.JsonMappingException import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.dataformat.yaml.YAMLFactory import com.fasterxml.jackson.module.kotlin.registerKotlinModule -import io.specmatic.core.Source -import io.specmatic.core.SourceProvider -import io.specmatic.core.SpecmaticConfig +import io.specmatic.core.* import io.specmatic.core.config.v1.SpecmaticConfigV1 import io.specmatic.core.config.v2.ContractConfig import io.specmatic.core.config.v2.ContractConfig.FileSystemContractSource @@ -15,7 +13,6 @@ import io.specmatic.core.config.v2.SpecmaticConfigV2 import io.specmatic.core.config.v3.Consumes import io.specmatic.core.config.v3.ContractConfigV2 import io.specmatic.core.config.v3.SpecmaticConfigV3 -import io.specmatic.core.loadSpecmaticConfig import io.specmatic.core.pattern.ContractException import io.specmatic.core.pattern.parsedJSON import org.assertj.core.api.Assertions.assertThat @@ -450,7 +447,7 @@ internal class SpecmaticConfigAllTest { } @Test - fun `should serialize SpecmaticConfig successfully when AllPatternsMandatory key is present`() { + fun `should convert config from v1 to v2 when AllPatternsMandatory key is present`() { val configYaml = """ allPatternsMandatory: true """.trimIndent() @@ -475,7 +472,7 @@ internal class SpecmaticConfigAllTest { } @Test - fun `should serialize SpecmaticConfig successfully when IgnoreInlineExamples key is present`() { + fun `should convert config from v1 to v2 when IgnoreInlineExamples key is present`() { val configYaml = """ ignoreInlineExamples: true """.trimIndent() @@ -485,4 +482,49 @@ internal class SpecmaticConfigAllTest { assertThat(configV2.ignoreInlineExamples).isTrue() } + + @Test + fun `should deserialize test configuration in SpecmaticConfig successfully key`(@TempDir tempDir: File) { + val configFile = tempDir.resolve("specmatic.yaml") + val configYaml = """ + test: + resiliencyTests: + enable: all + validateResponseValues: true + allowExtensibleSchema: true + timeoutInMilliseconds: 10 + """.trimIndent() + configFile.writeText(configYaml) + + val specmaticConfig = configFile.toSpecmaticConfig() + + specmaticConfig.apply { + assertThat(isResiliencyTestingEnabled()).isTrue() + assertThat(isResponseValueValidationEnabled()).isTrue() + assertThat(isExtensibleSchemaEnabled()).isTrue() + assertThat(getTestTimeoutInMilliseconds()).isEqualTo(10) + } + } + + @Test + fun `should convert config from v1 to v2 when test configuration is present`() { + val configYaml = """ + test: + resiliencyTests: + enable: all + validateResponseValues: true + allowExtensibleSchema: true + timeoutInMilliseconds: 10 + """.trimIndent() + + val configFromV1 = objectMapper.readValue(configYaml, SpecmaticConfigV1::class.java).transform() + val configV2 = SpecmaticConfigV2.loadFrom(configFromV1) as SpecmaticConfigV2 + + configV2.test!!.apply { + assertThat(getResiliencyTests().getEnableTestSuite()).isEqualTo(ResiliencyTestSuite.all) + assertThat(getValidateResponseValues()).isTrue() + assertThat(getAllowExtensibleSchema()).isTrue() + assertThat(getTimeoutInMilliseconds()).isEqualTo(10) + } + } } \ No newline at end of file