From ba0076a445248f2763c0afe1d7341b95a86c527d Mon Sep 17 00:00:00 2001 From: Joel Rosario Date: Thu, 27 Jun 2024 20:10:05 +0530 Subject: [PATCH 01/10] Add request context when validating contract test responses --- .../main/kotlin/in/specmatic/core/Scenario.kt | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/core/src/main/kotlin/in/specmatic/core/Scenario.kt b/core/src/main/kotlin/in/specmatic/core/Scenario.kt index 0e90bac20..823b90def 100644 --- a/core/src/main/kotlin/in/specmatic/core/Scenario.kt +++ b/core/src/main/kotlin/in/specmatic/core/Scenario.kt @@ -221,13 +221,31 @@ data class Scenario( fun generateHttpRequest(flagsBased: FlagsBased = DefaultStrategies): HttpRequest = scenarioBreadCrumb(this) { httpRequestPattern.generate(flagsBased.update(Resolver(expectedFacts, false, patterns))) } + fun matches(httpRequest: HttpRequest, httpResponse: HttpResponse, mismatchMessages: MismatchMessages = DefaultMismatchMessages, unexpectedKeyCheck: UnexpectedKeyCheck? = null): Result { + val resolver = updatedResolver(mismatchMessages, unexpectedKeyCheck).copy(context = RequestContext(httpRequest)) + + return matches(httpResponse, mismatchMessages, unexpectedKeyCheck, resolver) + } + fun matches(httpResponse: HttpResponse, mismatchMessages: MismatchMessages = DefaultMismatchMessages, unexpectedKeyCheck: UnexpectedKeyCheck? = null): Result { - val resolver = Resolver(expectedFacts, false, patterns).copy(mismatchMessages = mismatchMessages).let { - if(unexpectedKeyCheck != null) + val resolver = updatedResolver(mismatchMessages, unexpectedKeyCheck) + + return matches(httpResponse, mismatchMessages, unexpectedKeyCheck, resolver) + } + + private fun updatedResolver( + mismatchMessages: MismatchMessages, + unexpectedKeyCheck: UnexpectedKeyCheck? + ): Resolver { + return Resolver(expectedFacts, false, patterns).copy(mismatchMessages = mismatchMessages).let { + if (unexpectedKeyCheck != null) it.copy(findKeyErrorCheck = it.findKeyErrorCheck.copy(unexpectedKeyCheck = unexpectedKeyCheck)) else it } + } + + fun matches(httpResponse: HttpResponse, mismatchMessages: MismatchMessages = DefaultMismatchMessages, unexpectedKeyCheck: UnexpectedKeyCheck? = null, resolver: Resolver): Result { if (this.isNegative) { return if (is4xxResponse(httpResponse)) { @@ -591,7 +609,7 @@ fun executeTestAndReturnResultAndResponse( val response = testExecutor.execute(request) - val result = testResult(response, testScenario, flagsBased) + val result = testResult(request, response, testScenario, flagsBased) Pair(result.withBindings(testScenario.bindings, response), response) } catch (exception: Throwable) { @@ -601,6 +619,7 @@ fun executeTestAndReturnResultAndResponse( } private fun testResult( + request: HttpRequest, response: HttpResponse, testScenario: Scenario, flagsBased: FlagsBased? = null @@ -610,12 +629,12 @@ private fun testResult( response.specmaticResultHeaderValue() == "failure" -> Result.Failure(response.body.toStringLiteral()) .updateScenario(testScenario) response.body is JSONObjectValue && ignorable(response.body) -> Result.Success() - else -> testScenario.matches(response, ContractAndResponseMismatch, flagsBased?.unexpectedKeyCheck ?: ValidateUnexpectedKeys) - }.also { result -> - if (result is Result.Success && result.isPartialSuccess()) { - logger.log(" PARTIAL SUCCESS: ${result.partialSuccessMessage}") - logger.newLine() - } + else -> testScenario.matches(request, response, ContractAndResponseMismatch, flagsBased?.unexpectedKeyCheck ?: ValidateUnexpectedKeys) + } + + if (result is Result.Success && result.isPartialSuccess()) { + logger.log(" PARTIAL SUCCESS: ${result.partialSuccessMessage}") + logger.newLine() } return result From fe39740193de09ed7b20e4f4e8fd064530d77ea8 Mon Sep 17 00:00:00 2001 From: Joel Rosario Date: Sat, 29 Jun 2024 19:11:52 +0530 Subject: [PATCH 02/10] Cleaned up Feature.executeTests --- .../main/kotlin/in/specmatic/core/Feature.kt | 20 +++++++---- .../kotlin/in/specmatic/test/ContractTest.kt | 1 + .../kotlin/in/specmatic/test/ScenarioTest.kt | 8 ++++- .../test/ScenarioTestGenerationException.kt | 8 +++++ .../test/ScenarioTestGenerationFailure.kt | 4 +++ .../in/specmatic/conversions/OpenApiKtTest.kt | 6 ++-- .../specmatic/conversions/RegexSupportTest.kt | 10 +++--- .../core/ContractAsTestWithSamplesInTable.kt | 16 ++++----- .../LoadTestsFromExternalisedFiles.kt | 34 ++++++++----------- 9 files changed, 61 insertions(+), 46 deletions(-) diff --git a/core/src/main/kotlin/in/specmatic/core/Feature.kt b/core/src/main/kotlin/in/specmatic/core/Feature.kt index 36e76506a..931f25b11 100644 --- a/core/src/main/kotlin/in/specmatic/core/Feature.kt +++ b/core/src/main/kotlin/in/specmatic/core/Feature.kt @@ -189,16 +189,22 @@ data class Feature( } fun executeTests( - testExecutorFn: TestExecutor, + testExecutor: TestExecutor, suggestions: List = emptyList(), - scenarioNames: List = emptyList() - ): Results = - generateContractTestScenarios(suggestions) - .map { it.second.value } - .filter { scenarioNames.isEmpty() || scenarioNames.contains(it.name) } + testDescriptionFilter: List = emptyList() + ): Results { + return generateContractTests(suggestions) + .filter { contractTest -> + testDescriptionFilter.isEmpty() || + testDescriptionFilter.any { scenarioName -> + contractTest.testDescription().contains(scenarioName) + } + } .fold(Results()) { results, scenario -> - Results(results = results.results.plus(executeTest(scenario, testExecutorFn, flagsBased))) + val (result, _) = scenario.runTest(testExecutor) + Results(results = results.results.plus(result)) } + } fun setServerState(serverState: Map) { this.serverState = this.serverState.plus(serverState) diff --git a/core/src/main/kotlin/in/specmatic/test/ContractTest.kt b/core/src/main/kotlin/in/specmatic/test/ContractTest.kt index 085bda351..6c8baba7c 100644 --- a/core/src/main/kotlin/in/specmatic/test/ContractTest.kt +++ b/core/src/main/kotlin/in/specmatic/test/ContractTest.kt @@ -7,4 +7,5 @@ interface ContractTest { fun testResultRecord(result: Result, response: HttpResponse?): TestResultRecord? fun testDescription(): String fun runTest(testBaseURL: String, timeOut: Int): Pair + fun runTest(testExecutor: TestExecutor): Pair } diff --git a/core/src/main/kotlin/in/specmatic/test/ScenarioTest.kt b/core/src/main/kotlin/in/specmatic/test/ScenarioTest.kt index 23315c107..3ed374236 100644 --- a/core/src/main/kotlin/in/specmatic/test/ScenarioTest.kt +++ b/core/src/main/kotlin/in/specmatic/test/ScenarioTest.kt @@ -43,7 +43,13 @@ class ScenarioTest( } val httpClient = HttpClient(testBaseURL, log = log, timeout = timeOut) - val (result, response) = executeTestAndReturnResultAndResponse(scenario, httpClient, flagsBased) + + return runTest(httpClient) + } + + override fun runTest(testExecutor: TestExecutor): Pair { + + val (result, response) = executeTestAndReturnResultAndResponse(scenario, testExecutor, flagsBased) return Pair(result.updateScenario(scenario), response) } diff --git a/core/src/main/kotlin/in/specmatic/test/ScenarioTestGenerationException.kt b/core/src/main/kotlin/in/specmatic/test/ScenarioTestGenerationException.kt index cfec30710..51c52aee1 100644 --- a/core/src/main/kotlin/in/specmatic/test/ScenarioTestGenerationException.kt +++ b/core/src/main/kotlin/in/specmatic/test/ScenarioTestGenerationException.kt @@ -17,6 +17,14 @@ class ScenarioTestGenerationException(val scenario: Scenario, val e: Throwable, } override fun runTest(testBaseURL: String, timeOut: Int): Pair { + return error() + } + + override fun runTest(testExecutor: TestExecutor): Pair { + return error() + } + + fun error(): Pair { val result: Result = when(e) { is ContractException -> Result.Failure(message, e.failure(), breadCrumb = breadCrumb ?: "").updateScenario(scenario) else -> Result.Failure(message + " - " + exceptionCauseMessage(e), breadCrumb = breadCrumb ?: "").updateScenario(scenario) diff --git a/core/src/main/kotlin/in/specmatic/test/ScenarioTestGenerationFailure.kt b/core/src/main/kotlin/in/specmatic/test/ScenarioTestGenerationFailure.kt index 6e73483a4..91fbfbc2c 100644 --- a/core/src/main/kotlin/in/specmatic/test/ScenarioTestGenerationFailure.kt +++ b/core/src/main/kotlin/in/specmatic/test/ScenarioTestGenerationFailure.kt @@ -18,4 +18,8 @@ class ScenarioTestGenerationFailure(val scenario: Scenario, val failure: Result. return Pair(failure.updateScenario(scenario), null) } + override fun runTest(testExecutor: TestExecutor): Pair { + return Pair(failure.updateScenario(scenario), null) + } + } \ No newline at end of file diff --git a/core/src/test/kotlin/in/specmatic/conversions/OpenApiKtTest.kt b/core/src/test/kotlin/in/specmatic/conversions/OpenApiKtTest.kt index 346877d4b..f85316cae 100644 --- a/core/src/test/kotlin/in/specmatic/conversions/OpenApiKtTest.kt +++ b/core/src/test/kotlin/in/specmatic/conversions/OpenApiKtTest.kt @@ -1385,7 +1385,7 @@ Background: override fun setServerState(serverState: Map) { } }, - scenarioNames = listOf("create a pet. Response: pet response") + testDescriptionFilter = listOf("POST /pets -> 201") ) assertFalse(results.success()) @@ -1454,7 +1454,7 @@ Background: override fun setServerState(serverState: Map) { } }, - scenarioNames = listOf("create a pet. Response: pet response") + testDescriptionFilter = listOf("POST /pets -> 201") ) assertFalse(results.success()) @@ -1518,7 +1518,7 @@ Background: override fun setServerState(serverState: Map) { } }, - scenarioNames = listOf("create a pet. Response: pet response") + testDescriptionFilter = listOf("POST /pets -> 201") ) assertFalse(results.success()) diff --git a/core/src/test/kotlin/in/specmatic/conversions/RegexSupportTest.kt b/core/src/test/kotlin/in/specmatic/conversions/RegexSupportTest.kt index 8aa4d715e..f4c2f45ec 100644 --- a/core/src/test/kotlin/in/specmatic/conversions/RegexSupportTest.kt +++ b/core/src/test/kotlin/in/specmatic/conversions/RegexSupportTest.kt @@ -212,12 +212,10 @@ class RegexSupportTest { } } - assertThatThrownBy { feature.executeTests(mockTestClient) }.satisfies( - Consumer { - assertThat(it).isInstanceOf(ContractException::class.java) - assertThat(exceptionCauseMessage(it)).contains(regex) - } - ) + val results = feature.executeTests(mockTestClient) + + assertThat(results.success()).isFalse() + assertThat(results.report()).contains(regex) } @Nested diff --git a/core/src/test/kotlin/in/specmatic/core/ContractAsTestWithSamplesInTable.kt b/core/src/test/kotlin/in/specmatic/core/ContractAsTestWithSamplesInTable.kt index b2b8a37e8..b1e3f0773 100644 --- a/core/src/test/kotlin/in/specmatic/core/ContractAsTestWithSamplesInTable.kt +++ b/core/src/test/kotlin/in/specmatic/core/ContractAsTestWithSamplesInTable.kt @@ -1,12 +1,10 @@ package `in`.specmatic.core -import `in`.specmatic.core.pattern.ContractException import `in`.specmatic.core.pattern.NumberPattern import `in`.specmatic.core.pattern.StringPattern import `in`.specmatic.core.value.* import `in`.specmatic.test.TestExecutor import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -28,20 +26,16 @@ class ContractAsTestWithSamplesInTable { | 10 | 20 | 30 | | hello | 30 | 40 | """ - Assertions.assertThrows(ContractException::class.java) { jsonResponsesTestsShouldBeVerifiedAgainstTable(contractGherkin) } - } - @Throws(Throwable::class) - private fun jsonResponsesTestsShouldBeVerifiedAgainstTable(contractGherkin: String) { val contractBehaviour = parseGherkinStringToFeature(contractGherkin) val results = contractBehaviour.executeTests(object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { val accountId = request.queryParams.getOrElse("account_id") { - val pathParts = request.path!!.split("/".toRegex()).toTypedArray() + val pathParts = request.path!!.split("/".toRegex()).toTypedArray() pathParts[pathParts.size - 1] } assertEquals("GET", request.method) - assertTrue( NumberPattern().matches(NumberValue(accountId.toInt()), Resolver()) is Result.Success) + assertTrue(NumberPattern().matches(NumberValue(accountId.toInt()), Resolver()) is Result.Success) val headers: HashMap = object : HashMap() { init { put("Content-Type", "application/json") @@ -59,7 +53,9 @@ class ContractAsTestWithSamplesInTable { override fun setServerState(serverState: Map) {} }) - assertThat(results.success()).isTrue() + assertThat(results.successCount).isOne() + assertThat(results.failureCount).isOne() + assertThat(results.success()).withFailMessage(results.report()).isFalse() } @Test @@ -249,7 +245,7 @@ Feature: Contract for /balance API } } - val xmlResponseString: String = when(name.toStringLiteral()) { + val xmlResponseString: String = when (name.toStringLiteral()) { "John Doe" -> "10" "Jane Doe" -> "20" else -> fail("Expected name to be either \"John Doe\" or \"Jane Doe\", got ${name.toStringLiteral()}") diff --git a/core/src/test/kotlin/integration_tests/LoadTestsFromExternalisedFiles.kt b/core/src/test/kotlin/integration_tests/LoadTestsFromExternalisedFiles.kt index f38c38536..6870a5af5 100644 --- a/core/src/test/kotlin/integration_tests/LoadTestsFromExternalisedFiles.kt +++ b/core/src/test/kotlin/integration_tests/LoadTestsFromExternalisedFiles.kt @@ -74,30 +74,26 @@ class LoadTestsFromExternalisedFiles { fun `externalized tests should be validated`() { val feature = OpenApiSpecification.fromFile("src/test/resources/openapi/has_invalid_externalized_test.yaml").toFeature().loadExternalisedExamples() - assertThatThrownBy { - feature.executeTests(object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.path).isEqualTo("/order_action_figure") - assertThat(request.method).isEqualTo("POST") - assertThat(request.body).isEqualTo(parsedJSONObject("""{"name": "Master Yoda", "description": "Head of the Jedi Council"}""")) + val results = feature.executeTests(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.path).isEqualTo("/order_action_figure") + assertThat(request.method).isEqualTo("POST") + assertThat(request.body).isEqualTo(parsedJSONObject("""{"name": "Master Yoda", "description": "Head of the Jedi Council"}""")) - return HttpResponse.ok(parsedJSONObject("""{"id": 1}""")) - } + return HttpResponse.ok(parsedJSONObject("""{"id": 1}""")) + } - override fun setServerState(serverState: Map) { - } - }) - }.satisfies(Consumer { - assertThat(it).isInstanceOf(ContractException::class.java) - it as ContractException + override fun setServerState(serverState: Map) { + } + }) - assertThat(it.report()) - .contains(">> REQUEST.BODY.description") - .contains("10") + println(results.report()) - println(it.report()) - }) + assertThat(results.report()) + .contains(">> REQUEST.BODY.description") + .contains("10") + assertThat(results.success()).isFalse() } @Test From c0a00b2011c403864a84021d30ff50b42c424f17 Mon Sep 17 00:00:00 2001 From: Joel Rosario Date: Sat, 29 Jun 2024 19:27:05 +0530 Subject: [PATCH 03/10] Eliminated one of the executeTests functions --- .../main/kotlin/in/specmatic/core/Contract.kt | 8 +- .../main/kotlin/in/specmatic/core/Scenario.kt | 4 - .../conversions/OpenApiIntegrationTest.kt | 338 +++++++++--------- .../in/specmatic/conversions/OpenApiKtTest.kt | 40 +-- .../conversions/OpenApiSpecificationTest.kt | 112 +++--- .../kotlin/in/specmatic/core/ScenarioTest.kt | 17 +- 6 files changed, 257 insertions(+), 262 deletions(-) diff --git a/core/src/main/kotlin/in/specmatic/core/Contract.kt b/core/src/main/kotlin/in/specmatic/core/Contract.kt index 549204206..0763c2296 100644 --- a/core/src/main/kotlin/in/specmatic/core/Contract.kt +++ b/core/src/main/kotlin/in/specmatic/core/Contract.kt @@ -1,10 +1,9 @@ package `in`.specmatic.core -import `in`.specmatic.core.pattern.ContractException import `in`.specmatic.stub.HttpStub import `in`.specmatic.test.HttpClient -data class Contract(val contract: Feature) { +data class Contract(val feature: Feature) { companion object { fun fromGherkin(contractGherkin: String): Contract { return Contract(parseGherkinStringToFeature(contractGherkin)) @@ -14,11 +13,10 @@ data class Contract(val contract: Feature) { fun samples(fake: HttpStub) = samples(fake.endPoint) private fun samples(endPoint: String) { - val contractBehaviour = contract val httpClient = HttpClient(endPoint) - contractBehaviour.generateContractTestScenarios(emptyList()).map { it.second.value }.fold(Results()) { results, scenario -> - Results(results = results.results.plus(executeTest(scenario, httpClient)).toMutableList()) + feature.generateContractTests(emptyList()).fold(Results()) { results, contractTest -> + Results(results = results.results.plus(contractTest.runTest(httpClient).first)) } } } diff --git a/core/src/main/kotlin/in/specmatic/core/Scenario.kt b/core/src/main/kotlin/in/specmatic/core/Scenario.kt index 823b90def..a5ed44341 100644 --- a/core/src/main/kotlin/in/specmatic/core/Scenario.kt +++ b/core/src/main/kotlin/in/specmatic/core/Scenario.kt @@ -591,10 +591,6 @@ object ContractAndResponseMismatch : MismatchMessages { } } -fun executeTest(testScenario: Scenario, testExecutor: TestExecutor, resolverStrategies: FlagsBased = DefaultStrategies): Result { - return executeTestAndReturnResultAndResponse(testScenario, testExecutor, resolverStrategies).first -} - fun executeTestAndReturnResultAndResponse( testScenario: Scenario, testExecutor: TestExecutor, diff --git a/core/src/test/kotlin/in/specmatic/conversions/OpenApiIntegrationTest.kt b/core/src/test/kotlin/in/specmatic/conversions/OpenApiIntegrationTest.kt index 6297bb5a2..df46206e0 100644 --- a/core/src/test/kotlin/in/specmatic/conversions/OpenApiIntegrationTest.kt +++ b/core/src/test/kotlin/in/specmatic/conversions/OpenApiIntegrationTest.kt @@ -47,17 +47,17 @@ Examples: ) val contractTests = contract.generateContractTestScenarios(emptyList()).map { it.second.value } - val result = executeTest(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") - return HttpResponse.ok("success") - } + val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }) + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -67,18 +67,18 @@ Examples: val feature = parseContractFileToFeature("./src/test/resources/openapi/hello_with_oauth2_authorization_code_flow.yaml") val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } - val result = executeTest(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsKey(HttpHeaders.AUTHORIZATION) - assertThat(request.headers[HttpHeaders.AUTHORIZATION]).matches("Bearer (\\S+)") - return HttpResponse.ok("success") - } + val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsKey(HttpHeaders.AUTHORIZATION) + assertThat(request.headers[HttpHeaders.AUTHORIZATION]).matches("Bearer (\\S+)") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }) + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -90,17 +90,17 @@ Examples: securityConfiguration = newSecurityConfiguration(token) ) val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } - val result = executeTest(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsKey(HttpHeaders.AUTHORIZATION) - assertThat(request.headers[HttpHeaders.AUTHORIZATION]).matches("Bearer $token") - return HttpResponse.ok("success") - } + val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsKey(HttpHeaders.AUTHORIZATION) + assertThat(request.headers[HttpHeaders.AUTHORIZATION]).matches("Bearer $token") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } - }) + } + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -115,17 +115,17 @@ Examples: environmentAndPropertiesConfiguration = environmentAndPropertiesConfiguration ) val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } - val result = executeTest(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsKey(HttpHeaders.AUTHORIZATION) - assertThat(request.headers[HttpHeaders.AUTHORIZATION]).matches("Bearer ENV1234") - return HttpResponse.ok("success") - } + val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsKey(HttpHeaders.AUTHORIZATION) + assertThat(request.headers[HttpHeaders.AUTHORIZATION]).matches("Bearer ENV1234") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } - }) + } + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -220,17 +220,17 @@ Feature: Authenticated """.trimIndent(), sourceSpecPath ) val contractTests = contract.generateContractTestScenarios(emptyList()).map { it.second.value } - val result = executeTest(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") - return HttpResponse.ok("success") - } + val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }) + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -272,17 +272,17 @@ Feature: Authenticated """.trimIndent(), sourceSpecPath ) val contractTests = contract.generateContractTestScenarios(emptyList()).map { it.second.value } - val result = executeTest(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") - return HttpResponse.ok("success") - } + val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }) + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -324,17 +324,17 @@ Feature: Authenticated """.trimIndent(), sourceSpecPath ) val contractTests = contract.generateContractTestScenarios(emptyList()).map { it.second.value } - val result = executeTest(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") - return HttpResponse.ok("success") - } + val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }) + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -377,17 +377,17 @@ Feature: Authenticated ) val contractTests = contract.generateContractTestScenarios(emptyList()).map { it.second.value } - val result = executeTest(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsEntry("Authorization", "Bearer abc123") - return HttpResponse.ok("success") - } + val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsEntry("Authorization", "Bearer abc123") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }) + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -398,20 +398,20 @@ Feature: Authenticated val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } var requestMadeWithRandomlyGeneratedBearerToken = false contractTests.forEach { scenario -> - val result = executeTest(scenario, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - request.headers[HttpHeaders.AUTHORIZATION]?.takeIf { - it.matches(Regex("Bearer (\\S+)")) - }?.let { - requestMadeWithRandomlyGeneratedBearerToken = true - } - return HttpResponse.ok("success") - } - - override fun setServerState(serverState: Map) { - - } - }) + val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + request.headers[HttpHeaders.AUTHORIZATION]?.takeIf { + it.matches(Regex("Bearer (\\S+)")) + }?.let { + requestMadeWithRandomlyGeneratedBearerToken = true + } + return HttpResponse.ok("success") + } + + override fun setServerState(serverState: Map) { + + } + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } assertThat(requestMadeWithRandomlyGeneratedBearerToken).isTrue @@ -427,20 +427,20 @@ Feature: Authenticated val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } var requestMadeWithTokenFromSpecmaticJson = false contractTests.forEach { scenario -> - val result = executeTest(scenario, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - request.headers[HttpHeaders.AUTHORIZATION]?.takeIf { - it == "Bearer $token" - }?.let { - requestMadeWithTokenFromSpecmaticJson = true - } - return HttpResponse.ok("success") - } - - override fun setServerState(serverState: Map) { - - } - }) + val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + request.headers[HttpHeaders.AUTHORIZATION]?.takeIf { + it == "Bearer $token" + }?.let { + requestMadeWithTokenFromSpecmaticJson = true + } + return HttpResponse.ok("success") + } + + override fun setServerState(serverState: Map) { + + } + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } assertThat(requestMadeWithTokenFromSpecmaticJson).isTrue @@ -463,20 +463,20 @@ Feature: Authenticated val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } var requestMadeWithTokenFromSpecmaticJson = false contractTests.forEach { scenario -> - val result = executeTest(scenario, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - request.headers[HttpHeaders.AUTHORIZATION]?.takeIf { - it == "Bearer ENV1234" - }?.let { - requestMadeWithTokenFromSpecmaticJson = true - } - return HttpResponse.ok("success") - } - - override fun setServerState(serverState: Map) { - - } - }) + val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + request.headers[HttpHeaders.AUTHORIZATION]?.takeIf { + it == "Bearer ENV1234" + }?.let { + requestMadeWithTokenFromSpecmaticJson = true + } + return HttpResponse.ok("success") + } + + override fun setServerState(serverState: Map) { + + } + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } assertThat(requestMadeWithTokenFromSpecmaticJson).isTrue @@ -712,17 +712,17 @@ Feature: Authenticated ) val contractTests = contract.generateContractTestScenarios(emptyList()).map { it.second.value } - val result = executeTest(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.queryParams.containsEntry("apiKey", "abc123")).isTrue - return HttpResponse.ok("success") - } + val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.queryParams.containsEntry("apiKey", "abc123")).isTrue + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }) + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -775,17 +775,17 @@ Feature: Authenticated ) val contractTests = contract.generateContractTestScenarios(emptyList()).map { it.second.value } - val result = executeTest(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsEntry("X-API-KEY", "abc123") - return HttpResponse.ok("success") - } + val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsEntry("X-API-KEY", "abc123") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }) + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -800,20 +800,20 @@ Feature: Authenticated val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } var requestMadeWithApiKeyInHeaderFromSpecmaticJson = false contractTests.forEach { scenario -> - val result = executeTest(scenario, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - request.headers["X-API-KEY"]?.takeIf { - it == token - }?.let { - requestMadeWithApiKeyInHeaderFromSpecmaticJson = true - } - return HttpResponse.ok("success") - } - - override fun setServerState(serverState: Map) { - - } - }) + val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + request.headers["X-API-KEY"]?.takeIf { + it == token + }?.let { + requestMadeWithApiKeyInHeaderFromSpecmaticJson = true + } + return HttpResponse.ok("success") + } + + override fun setServerState(serverState: Map) { + + } + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } assertThat(requestMadeWithApiKeyInHeaderFromSpecmaticJson).isTrue @@ -837,20 +837,20 @@ Feature: Authenticated val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } var requestMadeWithApiKeyInHeaderFromSpecmaticJson = false contractTests.forEach { scenario -> - val result = executeTest(scenario, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - request.headers["X-API-KEY"]?.takeIf { - it == "ENV1234" - }?.let { - requestMadeWithApiKeyInHeaderFromSpecmaticJson = true - } - return HttpResponse.ok("success") - } - - override fun setServerState(serverState: Map) { - - } - }) + val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + request.headers["X-API-KEY"]?.takeIf { + it == "ENV1234" + }?.let { + requestMadeWithApiKeyInHeaderFromSpecmaticJson = true + } + return HttpResponse.ok("success") + } + + override fun setServerState(serverState: Map) { + + } + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } assertThat(requestMadeWithApiKeyInHeaderFromSpecmaticJson).isTrue @@ -866,22 +866,22 @@ Feature: Authenticated val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } var requestMadeWithApiKeyInQueryFromSpecmaticJson = false contractTests.forEach { scenario -> - val result = executeTest(scenario, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - if (request.queryParams.containsKey("apiKey")) { - request.queryParams.getValues("apiKey").first().takeIf { - it == token - }?.let { - requestMadeWithApiKeyInQueryFromSpecmaticJson = true - } - } - return HttpResponse.ok("success") - } - - override fun setServerState(serverState: Map) { - - } - }) + val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + if (request.queryParams.containsKey("apiKey")) { + request.queryParams.getValues("apiKey").first().takeIf { + it == token + }?.let { + requestMadeWithApiKeyInQueryFromSpecmaticJson = true + } + } + return HttpResponse.ok("success") + } + + override fun setServerState(serverState: Map) { + + } + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } assertThat(requestMadeWithApiKeyInQueryFromSpecmaticJson).isTrue diff --git a/core/src/test/kotlin/in/specmatic/conversions/OpenApiKtTest.kt b/core/src/test/kotlin/in/specmatic/conversions/OpenApiKtTest.kt index f85316cae..54b9d0cce 100644 --- a/core/src/test/kotlin/in/specmatic/conversions/OpenApiKtTest.kt +++ b/core/src/test/kotlin/in/specmatic/conversions/OpenApiKtTest.kt @@ -748,20 +748,20 @@ Feature: multipart file upload ) val contractTests = contract.generateContractTestScenarios(emptyList()).map { it.second.value } - val result = executeTest(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - val multipartFileValues = request.multiPartFormData.filterIsInstance() - assertThat(multipartFileValues.size).isEqualTo(1) - assertThat(multipartFileValues.first().name).isEqualTo("fileName") - assertThat(multipartFileValues.first().filename).matches(fileName) - return HttpResponse.ok("success") - } + val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + val multipartFileValues = request.multiPartFormData.filterIsInstance() + assertThat(multipartFileValues.size).isEqualTo(1) + assertThat(multipartFileValues.first().name).isEqualTo("fileName") + assertThat(multipartFileValues.first().filename).matches(fileName) + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }) + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -1670,17 +1670,17 @@ Scenario: zero should return not found var executed = false - val result = executeTest(feature.scenarios.first(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - executed = true - return if (request.queryParams.keys.containsAll(listOf("name", "message"))) HttpResponse.OK - else HttpResponse.ERROR_400 - } + val result = executeTestAndReturnResultAndResponse(feature.scenarios.first(), object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + executed = true + return if (request.queryParams.keys.containsAll(listOf("name", "message"))) HttpResponse.OK + else HttpResponse.ERROR_400 + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } - }) + } + }, DefaultStrategies).first assertThat(result).isInstanceOf(Result.Success::class.java) assertThat(executed).isTrue diff --git a/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt b/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt index 98054dcac..6e9ca5e77 100644 --- a/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt +++ b/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt @@ -5438,19 +5438,19 @@ paths: val results: List = feature.generateContractTestScenarios(emptyList()).toList().map { it.second.value }.map { - executeTest(it, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.body).isInstanceOf(JSONObjectValue::class.java) - - val body = request.body as JSONObjectValue - assertThat(body.jsonObject).hasSize(1) - assertThat(body.jsonObject).containsEntry("id", StringValue("abc123")) - return HttpResponse.OK - } - - override fun setServerState(serverState: Map) { - } - }) + executeTestAndReturnResultAndResponse(it, object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.body).isInstanceOf(JSONObjectValue::class.java) + + val body = request.body as JSONObjectValue + assertThat(body.jsonObject).hasSize(1) + assertThat(body.jsonObject).containsEntry("id", StringValue("abc123")) + return HttpResponse.OK + } + + override fun setServerState(serverState: Map) { + } + }, DefaultStrategies).first } assertThat(results).hasSize(1) @@ -5505,26 +5505,26 @@ paths: val results: List = feature.generateContractTestScenarios(emptyList()).toList().map { it.second.value }.map { - executeTest(it, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.formFields).containsKey("Data") - - var parsedValue: Value = JSONObjectValue() - assertThatCode { - parsedValue = parsedJSON(request.formFields["Data"]!!) - }.doesNotThrowAnyException() - - assertThat((parsedValue as JSONObjectValue).jsonObject).containsEntry( - "id", - StringValue("abc123") - ) - assertThat(request.formFields).hasSize(1) - return HttpResponse.OK - } - - override fun setServerState(serverState: Map) { - } - }) + executeTestAndReturnResultAndResponse(it, object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.formFields).containsKey("Data") + + var parsedValue: Value = JSONObjectValue() + assertThatCode { + parsedValue = parsedJSON(request.formFields["Data"]!!) + }.doesNotThrowAnyException() + + assertThat((parsedValue as JSONObjectValue).jsonObject).containsEntry( + "id", + StringValue("abc123") + ) + assertThat(request.formFields).hasSize(1) + return HttpResponse.OK + } + + override fun setServerState(serverState: Map) { + } + }, DefaultStrategies).first } assertThat(results).hasSize(1) @@ -5575,18 +5575,18 @@ paths: val results: List = feature.generateContractTestScenarios(emptyList()).toList().map { it.second.value }.map { - executeTest(it, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.formFields).containsKey("Data") - assertThat(request.formFields["Data"]).isEqualTo("abc123") - assertThat(request.formFields).hasSize(1) - - return HttpResponse.OK - } - - override fun setServerState(serverState: Map) { - } - }) + executeTestAndReturnResultAndResponse(it, object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.formFields).containsKey("Data") + assertThat(request.formFields["Data"]).isEqualTo("abc123") + assertThat(request.formFields).hasSize(1) + + return HttpResponse.OK + } + + override fun setServerState(serverState: Map) { + } + }, DefaultStrategies).first } assertThat(results).hasSize(1) @@ -5635,20 +5635,20 @@ paths: val results: List = feature.generateContractTestScenarios(emptyList()).toList().map { it.second.value }.map { - executeTest(it, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.multiPartFormData.first().name).isEqualTo("Data") + executeTestAndReturnResultAndResponse(it, object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.multiPartFormData.first().name).isEqualTo("Data") - val content = request.multiPartFormData.first() as MultiPartContentValue - assertThat(content.content.toStringLiteral()).isEqualTo("abc123") + val content = request.multiPartFormData.first() as MultiPartContentValue + assertThat(content.content.toStringLiteral()).isEqualTo("abc123") - assertThat(request.multiPartFormData).hasSize(1) - return HttpResponse.OK - } + assertThat(request.multiPartFormData).hasSize(1) + return HttpResponse.OK + } - override fun setServerState(serverState: Map) { - } - }) + override fun setServerState(serverState: Map) { + } + }, DefaultStrategies).first } assertThat(results).hasSize(1) diff --git a/core/src/test/kotlin/in/specmatic/core/ScenarioTest.kt b/core/src/test/kotlin/in/specmatic/core/ScenarioTest.kt index d1bcc99bb..81215b57b 100644 --- a/core/src/test/kotlin/in/specmatic/core/ScenarioTest.kt +++ b/core/src/test/kotlin/in/specmatic/core/ScenarioTest.kt @@ -515,14 +515,15 @@ paths: val contractTestScenarios = contract.generateContractTestScenarios(emptyList()).map { it.second.value } - val result: Result = executeTest(contractTestScenarios.first(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - return HttpResponse.ok("abc") - } - - override fun setServerState(serverState: Map) { - } - }) as Result.Failure + val result: Result = + executeTestAndReturnResultAndResponse(contractTestScenarios.first(), object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + return HttpResponse.ok("abc") + } + + override fun setServerState(serverState: Map) { + } + }, DefaultStrategies).first as Result.Failure assertThat(result.reportString()).contains("Contract expected") assertThat(result.reportString()).contains("response contained") From 11ec196c7649fb9f394adb630d4d15ebe2c741c2 Mon Sep 17 00:00:00 2001 From: Joel Rosario Date: Sat, 29 Jun 2024 19:44:35 +0530 Subject: [PATCH 04/10] Simplified the function calls to running contract tests in all tests down to calls to ContractTest --- .../main/kotlin/in/specmatic/core/Scenario.kt | 9 ++++++ .../conversions/OpenApiIntegrationTest.kt | 32 +++++++++---------- .../in/specmatic/conversions/OpenApiKtTest.kt | 4 +-- .../conversions/OpenApiSpecificationTest.kt | 8 ++--- .../kotlin/in/specmatic/core/ScenarioTest.kt | 2 +- 5 files changed, 32 insertions(+), 23 deletions(-) diff --git a/core/src/main/kotlin/in/specmatic/core/Scenario.kt b/core/src/main/kotlin/in/specmatic/core/Scenario.kt index a5ed44341..ad0bf8cbc 100644 --- a/core/src/main/kotlin/in/specmatic/core/Scenario.kt +++ b/core/src/main/kotlin/in/specmatic/core/Scenario.kt @@ -8,6 +8,7 @@ import `in`.specmatic.core.utilities.exceptionCauseMessage import `in`.specmatic.core.utilities.mapZip import `in`.specmatic.core.value.* import `in`.specmatic.stub.RequestContext +import `in`.specmatic.test.ContractTest import `in`.specmatic.test.TestExecutor object ContractAndStubMismatchMessages : MismatchMessages { @@ -591,6 +592,14 @@ object ContractAndResponseMismatch : MismatchMessages { } } +fun executeTestAndReturnResultAndResponse( + testScenario: ContractTest, + testExecutor: TestExecutor, + flagsBased: FlagsBased +): Pair { + return testScenario.runTest(testExecutor) +} + fun executeTestAndReturnResultAndResponse( testScenario: Scenario, testExecutor: TestExecutor, diff --git a/core/src/test/kotlin/in/specmatic/conversions/OpenApiIntegrationTest.kt b/core/src/test/kotlin/in/specmatic/conversions/OpenApiIntegrationTest.kt index df46206e0..253633d1e 100644 --- a/core/src/test/kotlin/in/specmatic/conversions/OpenApiIntegrationTest.kt +++ b/core/src/test/kotlin/in/specmatic/conversions/OpenApiIntegrationTest.kt @@ -46,7 +46,7 @@ Examples: """.trimIndent(), sourceSpecPath ) - val contractTests = contract.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = contract.generateContractTests(emptyList()) val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") @@ -66,7 +66,7 @@ Examples: fun `should generate test with oauth2 authorization code security scheme with random token in authorization header when no example exists`() { val feature = parseContractFileToFeature("./src/test/resources/openapi/hello_with_oauth2_authorization_code_flow.yaml") - val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = feature.generateContractTests(emptyList()) val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { assertThat(request.headers).containsKey(HttpHeaders.AUTHORIZATION) @@ -89,7 +89,7 @@ Examples: "./src/test/resources/openapi/hello_with_oauth2_authorization_code_flow.yaml", securityConfiguration = newSecurityConfiguration(token) ) - val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = feature.generateContractTests(emptyList()) val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { assertThat(request.headers).containsKey(HttpHeaders.AUTHORIZATION) @@ -114,7 +114,7 @@ Examples: "./src/test/resources/openapi/hello_with_oauth2_authorization_code_flow.yaml", environmentAndPropertiesConfiguration = environmentAndPropertiesConfiguration ) - val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = feature.generateContractTests(emptyList()) val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { assertThat(request.headers).containsKey(HttpHeaders.AUTHORIZATION) @@ -219,7 +219,7 @@ Feature: Authenticated | Bearer abc123 | 10 | """.trimIndent(), sourceSpecPath ) - val contractTests = contract.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = contract.generateContractTests(emptyList()) val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") @@ -271,7 +271,7 @@ Feature: Authenticated | Bearer abc123 | 10 | """.trimIndent(), sourceSpecPath ) - val contractTests = contract.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = contract.generateContractTests(emptyList()) val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") @@ -323,7 +323,7 @@ Feature: Authenticated | Bearer abc123 | 10 | """.trimIndent(), sourceSpecPath ) - val contractTests = contract.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = contract.generateContractTests(emptyList()) val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") @@ -376,7 +376,7 @@ Feature: Authenticated """.trimIndent(), sourceSpecPath ) - val contractTests = contract.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = contract.generateContractTests(emptyList()) val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { assertThat(request.headers).containsEntry("Authorization", "Bearer abc123") @@ -395,7 +395,7 @@ Feature: Authenticated @Test fun `should generate test with bearer security scheme with random token in authorization header when no example exists`() { val feature = parseContractFileToFeature("./src/test/resources/openapi/authenticated.yaml") - val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = feature.generateContractTests(emptyList()) var requestMadeWithRandomlyGeneratedBearerToken = false contractTests.forEach { scenario -> val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { @@ -424,7 +424,7 @@ Feature: Authenticated "./src/test/resources/openapi/authenticated.yaml", securityConfiguration = securityConfigurationForBearerScheme(token) ) - val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = feature.generateContractTests(emptyList()) var requestMadeWithTokenFromSpecmaticJson = false contractTests.forEach { scenario -> val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { @@ -460,7 +460,7 @@ Feature: Authenticated "./src/test/resources/openapi/authenticated.yaml", environmentAndPropertiesConfiguration = environmentAndPropertiesConfiguration ) - val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = feature.generateContractTests(emptyList()) var requestMadeWithTokenFromSpecmaticJson = false contractTests.forEach { scenario -> val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { @@ -711,7 +711,7 @@ Feature: Authenticated """.trimIndent(), sourceSpecPath ) - val contractTests = contract.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = contract.generateContractTests(emptyList()) val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { assertThat(request.queryParams.containsEntry("apiKey", "abc123")).isTrue @@ -774,7 +774,7 @@ Feature: Authenticated """.trimIndent(), sourceSpecPath ) - val contractTests = contract.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = contract.generateContractTests(emptyList()) val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { assertThat(request.headers).containsEntry("X-API-KEY", "abc123") @@ -797,7 +797,7 @@ Feature: Authenticated "./src/test/resources/openapi/authenticated.yaml", securityConfiguration = securityConfigurationForApiKeyInHeaderScheme(token) ) - val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = feature.generateContractTests(emptyList()) var requestMadeWithApiKeyInHeaderFromSpecmaticJson = false contractTests.forEach { scenario -> val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { @@ -834,7 +834,7 @@ Feature: Authenticated "./src/test/resources/openapi/authenticated.yaml", environmentAndPropertiesConfiguration = environmentAndPropertiesConfiguration ) - val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = feature.generateContractTests(emptyList()) var requestMadeWithApiKeyInHeaderFromSpecmaticJson = false contractTests.forEach { scenario -> val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { @@ -863,7 +863,7 @@ Feature: Authenticated "./src/test/resources/openapi/authenticated.yaml", securityConfiguration = securityConfigurationForApiKeyInQueryScheme(token) ) - val contractTests = feature.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = feature.generateContractTests(emptyList()) var requestMadeWithApiKeyInQueryFromSpecmaticJson = false contractTests.forEach { scenario -> val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { diff --git a/core/src/test/kotlin/in/specmatic/conversions/OpenApiKtTest.kt b/core/src/test/kotlin/in/specmatic/conversions/OpenApiKtTest.kt index 54b9d0cce..678dba399 100644 --- a/core/src/test/kotlin/in/specmatic/conversions/OpenApiKtTest.kt +++ b/core/src/test/kotlin/in/specmatic/conversions/OpenApiKtTest.kt @@ -747,7 +747,7 @@ Feature: multipart file upload """.trimIndent(), sourceSpecPath ) - val contractTests = contract.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTests = contract.generateContractTests(emptyList()) val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { val multipartFileValues = request.multiPartFormData.filterIsInstance() @@ -1670,7 +1670,7 @@ Scenario: zero should return not found var executed = false - val result = executeTestAndReturnResultAndResponse(feature.scenarios.first(), object : TestExecutor { + val result = executeTestAndReturnResultAndResponse(`in`.specmatic.test.ScenarioTest(feature.scenarios.first(), DefaultStrategies), object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { executed = true return if (request.queryParams.keys.containsAll(listOf("name", "message"))) HttpResponse.OK diff --git a/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt b/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt index 6e9ca5e77..925d2714d 100644 --- a/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt +++ b/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt @@ -5437,7 +5437,7 @@ paths: val feature = OpenApiSpecification.fromYAML(contractString, "").toFeature() val results: List = - feature.generateContractTestScenarios(emptyList()).toList().map { it.second.value }.map { + feature.generateContractTests(emptyList()).toList().map { executeTestAndReturnResultAndResponse(it, object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { assertThat(request.body).isInstanceOf(JSONObjectValue::class.java) @@ -5504,7 +5504,7 @@ paths: val feature = OpenApiSpecification.fromYAML(contractString, "").toFeature() val results: List = - feature.generateContractTestScenarios(emptyList()).toList().map { it.second.value }.map { + feature.generateContractTests(emptyList()).toList().map { executeTestAndReturnResultAndResponse(it, object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { assertThat(request.formFields).containsKey("Data") @@ -5574,7 +5574,7 @@ paths: val feature = OpenApiSpecification.fromYAML(contractString, "").toFeature() val results: List = - feature.generateContractTestScenarios(emptyList()).toList().map { it.second.value }.map { + feature.generateContractTests(emptyList()).toList().map { executeTestAndReturnResultAndResponse(it, object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { assertThat(request.formFields).containsKey("Data") @@ -5634,7 +5634,7 @@ paths: val feature = OpenApiSpecification.fromYAML(contractString, "").toFeature() val results: List = - feature.generateContractTestScenarios(emptyList()).toList().map { it.second.value }.map { + feature.generateContractTests(emptyList()).toList().map { executeTestAndReturnResultAndResponse(it, object : TestExecutor { override fun execute(request: HttpRequest): HttpResponse { assertThat(request.multiPartFormData.first().name).isEqualTo("Data") diff --git a/core/src/test/kotlin/in/specmatic/core/ScenarioTest.kt b/core/src/test/kotlin/in/specmatic/core/ScenarioTest.kt index 81215b57b..8d399fd69 100644 --- a/core/src/test/kotlin/in/specmatic/core/ScenarioTest.kt +++ b/core/src/test/kotlin/in/specmatic/core/ScenarioTest.kt @@ -513,7 +513,7 @@ paths: """.trimIndent(), "" ).toFeature() - val contractTestScenarios = contract.generateContractTestScenarios(emptyList()).map { it.second.value } + val contractTestScenarios = contract.generateContractTests(emptyList()) val result: Result = executeTestAndReturnResultAndResponse(contractTestScenarios.first(), object : TestExecutor { From 2d79d4768109826dc4ce503ce8dd856a2c011f0b Mon Sep 17 00:00:00 2001 From: Joel Rosario Date: Sat, 29 Jun 2024 19:48:57 +0530 Subject: [PATCH 05/10] Moved functions for running contract tests into the ScenarioTest class --- .../main/kotlin/in/specmatic/core/Feature.kt | 6 +- .../main/kotlin/in/specmatic/core/Scenario.kt | 59 --- .../{ScenarioTest.kt => ScenarioAsTest.kt} | 56 ++- .../conversions/OpenApiIntegrationTest.kt | 338 +++++++++--------- .../in/specmatic/conversions/OpenApiKtTest.kt | 41 +-- .../conversions/OpenApiSpecificationTest.kt | 112 +++--- .../{ScenarioTest.kt => ScenarioAsTest.kt} | 18 +- 7 files changed, 313 insertions(+), 317 deletions(-) rename core/src/main/kotlin/in/specmatic/test/{ScenarioTest.kt => ScenarioAsTest.kt} (50%) rename core/src/test/kotlin/in/specmatic/core/{ScenarioTest.kt => ScenarioAsTest.kt} (97%) diff --git a/core/src/main/kotlin/in/specmatic/core/Feature.kt b/core/src/main/kotlin/in/specmatic/core/Feature.kt index 931f25b11..b5e28cd61 100644 --- a/core/src/main/kotlin/in/specmatic/core/Feature.kt +++ b/core/src/main/kotlin/in/specmatic/core/Feature.kt @@ -200,8 +200,8 @@ data class Feature( contractTest.testDescription().contains(scenarioName) } } - .fold(Results()) { results, scenario -> - val (result, _) = scenario.runTest(testExecutor) + .fold(Results()) { results, contractTest -> + val (result, _) = contractTest.runTest(testExecutor) Results(results = results.results.plus(result)) } } @@ -306,7 +306,7 @@ data class Feature( return generateContractTestScenarios(suggestions).map { (originalScenario, returnValue) -> returnValue.realise( hasValue = { concreteTestScenario, comment -> - ScenarioTest( + ScenarioAsTest( concreteTestScenario, flagsBased, concreteTestScenario.sourceProvider, diff --git a/core/src/main/kotlin/in/specmatic/core/Scenario.kt b/core/src/main/kotlin/in/specmatic/core/Scenario.kt index ad0bf8cbc..0c7d11536 100644 --- a/core/src/main/kotlin/in/specmatic/core/Scenario.kt +++ b/core/src/main/kotlin/in/specmatic/core/Scenario.kt @@ -591,62 +591,3 @@ object ContractAndResponseMismatch : MismatchMessages { } named $keyName in the specification was not found in the response" } } - -fun executeTestAndReturnResultAndResponse( - testScenario: ContractTest, - testExecutor: TestExecutor, - flagsBased: FlagsBased -): Pair { - return testScenario.runTest(testExecutor) -} - -fun executeTestAndReturnResultAndResponse( - testScenario: Scenario, - testExecutor: TestExecutor, - flagsBased: FlagsBased -): Pair { - val request = testScenario.generateHttpRequest(flagsBased) - - return try { - testExecutor.setServerState(testScenario.serverState) - - testExecutor.preExecuteScenario(testScenario, request) - - val response = testExecutor.execute(request) - - val result = testResult(request, response, testScenario, flagsBased) - - Pair(result.withBindings(testScenario.bindings, response), response) - } catch (exception: Throwable) { - Pair(Result.Failure(exceptionCauseMessage(exception)) - .also { failure -> failure.updateScenario(testScenario) }, null) - } -} - -private fun testResult( - request: HttpRequest, - response: HttpResponse, - testScenario: Scenario, - flagsBased: FlagsBased? = null -): Result { - - val result = when { - response.specmaticResultHeaderValue() == "failure" -> Result.Failure(response.body.toStringLiteral()) - .updateScenario(testScenario) - response.body is JSONObjectValue && ignorable(response.body) -> Result.Success() - else -> testScenario.matches(request, response, ContractAndResponseMismatch, flagsBased?.unexpectedKeyCheck ?: ValidateUnexpectedKeys) - } - - if (result is Result.Success && result.isPartialSuccess()) { - logger.log(" PARTIAL SUCCESS: ${result.partialSuccessMessage}") - logger.newLine() - } - - return result -} - -fun ignorable(body: JSONObjectValue): Boolean { - return Flags.customResponse() && - (body.findFirstChildByPath("resultStatus.status")?.toStringLiteral() == "FAILED" && - (body.findFirstChildByPath("resultStatus.errorCode")?.toStringLiteral() == "INVALID_REQUEST")) -} diff --git a/core/src/main/kotlin/in/specmatic/test/ScenarioTest.kt b/core/src/main/kotlin/in/specmatic/test/ScenarioAsTest.kt similarity index 50% rename from core/src/main/kotlin/in/specmatic/test/ScenarioTest.kt rename to core/src/main/kotlin/in/specmatic/test/ScenarioAsTest.kt index 3ed374236..e7f219861 100644 --- a/core/src/main/kotlin/in/specmatic/test/ScenarioTest.kt +++ b/core/src/main/kotlin/in/specmatic/test/ScenarioAsTest.kt @@ -5,8 +5,10 @@ import `in`.specmatic.core.* import `in`.specmatic.core.log.HttpLogMessage import `in`.specmatic.core.log.LogMessage import `in`.specmatic.core.log.logger +import `in`.specmatic.core.utilities.exceptionCauseMessage +import `in`.specmatic.core.value.JSONObjectValue -class ScenarioTest( +class ScenarioAsTest( val scenario: Scenario, private val flagsBased: FlagsBased, private val sourceProvider: String? = null, @@ -59,6 +61,58 @@ class ScenarioTest( } } + private fun executeTestAndReturnResultAndResponse( + testScenario: Scenario, + testExecutor: TestExecutor, + flagsBased: FlagsBased + ): Pair { + val request = testScenario.generateHttpRequest(flagsBased) + + return try { + testExecutor.setServerState(testScenario.serverState) + + testExecutor.preExecuteScenario(testScenario, request) + + val response = testExecutor.execute(request) + + val result = testResult(request, response, testScenario, flagsBased) + + Pair(result.withBindings(testScenario.bindings, response), response) + } catch (exception: Throwable) { + Pair( + Result.Failure(exceptionCauseMessage(exception)) + .also { failure -> failure.updateScenario(testScenario) }, null) + } + } + + private fun testResult( + request: HttpRequest, + response: HttpResponse, + testScenario: Scenario, + flagsBased: FlagsBased? = null + ): Result { + + val result = when { + response.specmaticResultHeaderValue() == "failure" -> Result.Failure(response.body.toStringLiteral()) + .updateScenario(testScenario) + response.body is JSONObjectValue && ignorable(response.body) -> Result.Success() + else -> testScenario.matches(request, response, ContractAndResponseMismatch, flagsBased?.unexpectedKeyCheck ?: ValidateUnexpectedKeys) + } + + if (result is Result.Success && result.isPartialSuccess()) { + logger.log(" PARTIAL SUCCESS: ${result.partialSuccessMessage}") + logger.newLine() + } + + return result + } + + fun ignorable(body: JSONObjectValue): Boolean { + return Flags.customResponse() && + (body.findFirstChildByPath("resultStatus.status")?.toStringLiteral() == "FAILED" && + (body.findFirstChildByPath("resultStatus.errorCode")?.toStringLiteral() == "INVALID_REQUEST")) + } + } private fun LogMessage.withComment(comment: String?): LogMessage { diff --git a/core/src/test/kotlin/in/specmatic/conversions/OpenApiIntegrationTest.kt b/core/src/test/kotlin/in/specmatic/conversions/OpenApiIntegrationTest.kt index 253633d1e..8fdb48726 100644 --- a/core/src/test/kotlin/in/specmatic/conversions/OpenApiIntegrationTest.kt +++ b/core/src/test/kotlin/in/specmatic/conversions/OpenApiIntegrationTest.kt @@ -47,17 +47,17 @@ Examples: ) val contractTests = contract.generateContractTests(emptyList()) - val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") - return HttpResponse.ok("success") - } + val result = contractTests.single().runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }, DefaultStrategies).first + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -67,18 +67,18 @@ Examples: val feature = parseContractFileToFeature("./src/test/resources/openapi/hello_with_oauth2_authorization_code_flow.yaml") val contractTests = feature.generateContractTests(emptyList()) - val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsKey(HttpHeaders.AUTHORIZATION) - assertThat(request.headers[HttpHeaders.AUTHORIZATION]).matches("Bearer (\\S+)") - return HttpResponse.ok("success") - } + val result = contractTests.single().runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsKey(HttpHeaders.AUTHORIZATION) + assertThat(request.headers[HttpHeaders.AUTHORIZATION]).matches("Bearer (\\S+)") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }, DefaultStrategies).first + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -90,17 +90,17 @@ Examples: securityConfiguration = newSecurityConfiguration(token) ) val contractTests = feature.generateContractTests(emptyList()) - val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsKey(HttpHeaders.AUTHORIZATION) - assertThat(request.headers[HttpHeaders.AUTHORIZATION]).matches("Bearer $token") - return HttpResponse.ok("success") - } + val result = contractTests.single().runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsKey(HttpHeaders.AUTHORIZATION) + assertThat(request.headers[HttpHeaders.AUTHORIZATION]).matches("Bearer $token") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } - }, DefaultStrategies).first + } + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -115,17 +115,17 @@ Examples: environmentAndPropertiesConfiguration = environmentAndPropertiesConfiguration ) val contractTests = feature.generateContractTests(emptyList()) - val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsKey(HttpHeaders.AUTHORIZATION) - assertThat(request.headers[HttpHeaders.AUTHORIZATION]).matches("Bearer ENV1234") - return HttpResponse.ok("success") - } + val result = contractTests.single().runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsKey(HttpHeaders.AUTHORIZATION) + assertThat(request.headers[HttpHeaders.AUTHORIZATION]).matches("Bearer ENV1234") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } - }, DefaultStrategies).first + } + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -220,17 +220,17 @@ Feature: Authenticated """.trimIndent(), sourceSpecPath ) val contractTests = contract.generateContractTests(emptyList()) - val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") - return HttpResponse.ok("success") - } + val result = contractTests.single().runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }, DefaultStrategies).first + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -272,17 +272,17 @@ Feature: Authenticated """.trimIndent(), sourceSpecPath ) val contractTests = contract.generateContractTests(emptyList()) - val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") - return HttpResponse.ok("success") - } + val result = contractTests.single().runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }, DefaultStrategies).first + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -324,17 +324,17 @@ Feature: Authenticated """.trimIndent(), sourceSpecPath ) val contractTests = contract.generateContractTests(emptyList()) - val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") - return HttpResponse.ok("success") - } + val result = contractTests.single().runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsEntry(HttpHeaders.AUTHORIZATION, "Bearer abc123") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }, DefaultStrategies).first + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -377,17 +377,17 @@ Feature: Authenticated ) val contractTests = contract.generateContractTests(emptyList()) - val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsEntry("Authorization", "Bearer abc123") - return HttpResponse.ok("success") - } + val result = contractTests.single().runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsEntry("Authorization", "Bearer abc123") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }, DefaultStrategies).first + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -398,20 +398,20 @@ Feature: Authenticated val contractTests = feature.generateContractTests(emptyList()) var requestMadeWithRandomlyGeneratedBearerToken = false contractTests.forEach { scenario -> - val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - request.headers[HttpHeaders.AUTHORIZATION]?.takeIf { - it.matches(Regex("Bearer (\\S+)")) - }?.let { - requestMadeWithRandomlyGeneratedBearerToken = true - } - return HttpResponse.ok("success") - } - - override fun setServerState(serverState: Map) { - - } - }, DefaultStrategies).first + val result = scenario.runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + request.headers[HttpHeaders.AUTHORIZATION]?.takeIf { + it.matches(Regex("Bearer (\\S+)")) + }?.let { + requestMadeWithRandomlyGeneratedBearerToken = true + } + return HttpResponse.ok("success") + } + + override fun setServerState(serverState: Map) { + + } + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } assertThat(requestMadeWithRandomlyGeneratedBearerToken).isTrue @@ -427,20 +427,20 @@ Feature: Authenticated val contractTests = feature.generateContractTests(emptyList()) var requestMadeWithTokenFromSpecmaticJson = false contractTests.forEach { scenario -> - val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - request.headers[HttpHeaders.AUTHORIZATION]?.takeIf { - it == "Bearer $token" - }?.let { - requestMadeWithTokenFromSpecmaticJson = true - } - return HttpResponse.ok("success") - } - - override fun setServerState(serverState: Map) { - - } - }, DefaultStrategies).first + val result = scenario.runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + request.headers[HttpHeaders.AUTHORIZATION]?.takeIf { + it == "Bearer $token" + }?.let { + requestMadeWithTokenFromSpecmaticJson = true + } + return HttpResponse.ok("success") + } + + override fun setServerState(serverState: Map) { + + } + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } assertThat(requestMadeWithTokenFromSpecmaticJson).isTrue @@ -463,20 +463,20 @@ Feature: Authenticated val contractTests = feature.generateContractTests(emptyList()) var requestMadeWithTokenFromSpecmaticJson = false contractTests.forEach { scenario -> - val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - request.headers[HttpHeaders.AUTHORIZATION]?.takeIf { - it == "Bearer ENV1234" - }?.let { - requestMadeWithTokenFromSpecmaticJson = true - } - return HttpResponse.ok("success") - } - - override fun setServerState(serverState: Map) { - - } - }, DefaultStrategies).first + val result = scenario.runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + request.headers[HttpHeaders.AUTHORIZATION]?.takeIf { + it == "Bearer ENV1234" + }?.let { + requestMadeWithTokenFromSpecmaticJson = true + } + return HttpResponse.ok("success") + } + + override fun setServerState(serverState: Map) { + + } + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } assertThat(requestMadeWithTokenFromSpecmaticJson).isTrue @@ -712,17 +712,17 @@ Feature: Authenticated ) val contractTests = contract.generateContractTests(emptyList()) - val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.queryParams.containsEntry("apiKey", "abc123")).isTrue - return HttpResponse.ok("success") - } + val result = contractTests.single().runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.queryParams.containsEntry("apiKey", "abc123")).isTrue + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }, DefaultStrategies).first + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -775,17 +775,17 @@ Feature: Authenticated ) val contractTests = contract.generateContractTests(emptyList()) - val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.headers).containsEntry("X-API-KEY", "abc123") - return HttpResponse.ok("success") - } + val result = contractTests.single().runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.headers).containsEntry("X-API-KEY", "abc123") + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }, DefaultStrategies).first + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -800,20 +800,20 @@ Feature: Authenticated val contractTests = feature.generateContractTests(emptyList()) var requestMadeWithApiKeyInHeaderFromSpecmaticJson = false contractTests.forEach { scenario -> - val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - request.headers["X-API-KEY"]?.takeIf { - it == token - }?.let { - requestMadeWithApiKeyInHeaderFromSpecmaticJson = true - } - return HttpResponse.ok("success") - } - - override fun setServerState(serverState: Map) { - - } - }, DefaultStrategies).first + val result = scenario.runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + request.headers["X-API-KEY"]?.takeIf { + it == token + }?.let { + requestMadeWithApiKeyInHeaderFromSpecmaticJson = true + } + return HttpResponse.ok("success") + } + + override fun setServerState(serverState: Map) { + + } + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } assertThat(requestMadeWithApiKeyInHeaderFromSpecmaticJson).isTrue @@ -837,20 +837,20 @@ Feature: Authenticated val contractTests = feature.generateContractTests(emptyList()) var requestMadeWithApiKeyInHeaderFromSpecmaticJson = false contractTests.forEach { scenario -> - val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - request.headers["X-API-KEY"]?.takeIf { - it == "ENV1234" - }?.let { - requestMadeWithApiKeyInHeaderFromSpecmaticJson = true - } - return HttpResponse.ok("success") - } - - override fun setServerState(serverState: Map) { - - } - }, DefaultStrategies).first + val result = scenario.runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + request.headers["X-API-KEY"]?.takeIf { + it == "ENV1234" + }?.let { + requestMadeWithApiKeyInHeaderFromSpecmaticJson = true + } + return HttpResponse.ok("success") + } + + override fun setServerState(serverState: Map) { + + } + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } assertThat(requestMadeWithApiKeyInHeaderFromSpecmaticJson).isTrue @@ -866,22 +866,22 @@ Feature: Authenticated val contractTests = feature.generateContractTests(emptyList()) var requestMadeWithApiKeyInQueryFromSpecmaticJson = false contractTests.forEach { scenario -> - val result = executeTestAndReturnResultAndResponse(scenario, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - if (request.queryParams.containsKey("apiKey")) { - request.queryParams.getValues("apiKey").first().takeIf { - it == token - }?.let { - requestMadeWithApiKeyInQueryFromSpecmaticJson = true - } - } - return HttpResponse.ok("success") - } - - override fun setServerState(serverState: Map) { - - } - }, DefaultStrategies).first + val result = scenario.runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + if (request.queryParams.containsKey("apiKey")) { + request.queryParams.getValues("apiKey").first().takeIf { + it == token + }?.let { + requestMadeWithApiKeyInQueryFromSpecmaticJson = true + } + } + return HttpResponse.ok("success") + } + + override fun setServerState(serverState: Map) { + + } + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } assertThat(requestMadeWithApiKeyInQueryFromSpecmaticJson).isTrue diff --git a/core/src/test/kotlin/in/specmatic/conversions/OpenApiKtTest.kt b/core/src/test/kotlin/in/specmatic/conversions/OpenApiKtTest.kt index 678dba399..1d79fb3af 100644 --- a/core/src/test/kotlin/in/specmatic/conversions/OpenApiKtTest.kt +++ b/core/src/test/kotlin/in/specmatic/conversions/OpenApiKtTest.kt @@ -748,20 +748,20 @@ Feature: multipart file upload ) val contractTests = contract.generateContractTests(emptyList()) - val result = executeTestAndReturnResultAndResponse(contractTests.single(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - val multipartFileValues = request.multiPartFormData.filterIsInstance() - assertThat(multipartFileValues.size).isEqualTo(1) - assertThat(multipartFileValues.first().name).isEqualTo("fileName") - assertThat(multipartFileValues.first().filename).matches(fileName) - return HttpResponse.ok("success") - } + val result = contractTests.single().runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + val multipartFileValues = request.multiPartFormData.filterIsInstance() + assertThat(multipartFileValues.size).isEqualTo(1) + assertThat(multipartFileValues.first().name).isEqualTo("fileName") + assertThat(multipartFileValues.first().filename).matches(fileName) + return HttpResponse.ok("success") + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } + } - }, DefaultStrategies).first + }).first assertThat(result).isInstanceOf(Result.Success::class.java) } @@ -1670,17 +1670,18 @@ Scenario: zero should return not found var executed = false - val result = executeTestAndReturnResultAndResponse(`in`.specmatic.test.ScenarioTest(feature.scenarios.first(), DefaultStrategies), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - executed = true - return if (request.queryParams.keys.containsAll(listOf("name", "message"))) HttpResponse.OK - else HttpResponse.ERROR_400 - } + val result = `in`.specmatic.test.ScenarioAsTest(feature.scenarios.first(), DefaultStrategies) + .runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + executed = true + return if (request.queryParams.keys.containsAll(listOf("name", "message"))) HttpResponse.OK + else HttpResponse.ERROR_400 + } - override fun setServerState(serverState: Map) { + override fun setServerState(serverState: Map) { - } - }, DefaultStrategies).first + } + }).first assertThat(result).isInstanceOf(Result.Success::class.java) assertThat(executed).isTrue diff --git a/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt b/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt index 925d2714d..a1141e681 100644 --- a/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt +++ b/core/src/test/kotlin/in/specmatic/conversions/OpenApiSpecificationTest.kt @@ -5438,19 +5438,19 @@ paths: val results: List = feature.generateContractTests(emptyList()).toList().map { - executeTestAndReturnResultAndResponse(it, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.body).isInstanceOf(JSONObjectValue::class.java) - - val body = request.body as JSONObjectValue - assertThat(body.jsonObject).hasSize(1) - assertThat(body.jsonObject).containsEntry("id", StringValue("abc123")) - return HttpResponse.OK - } - - override fun setServerState(serverState: Map) { - } - }, DefaultStrategies).first + it.runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.body).isInstanceOf(JSONObjectValue::class.java) + + val body = request.body as JSONObjectValue + assertThat(body.jsonObject).hasSize(1) + assertThat(body.jsonObject).containsEntry("id", StringValue("abc123")) + return HttpResponse.OK + } + + override fun setServerState(serverState: Map) { + } + }).first } assertThat(results).hasSize(1) @@ -5505,26 +5505,26 @@ paths: val results: List = feature.generateContractTests(emptyList()).toList().map { - executeTestAndReturnResultAndResponse(it, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.formFields).containsKey("Data") - - var parsedValue: Value = JSONObjectValue() - assertThatCode { - parsedValue = parsedJSON(request.formFields["Data"]!!) - }.doesNotThrowAnyException() - - assertThat((parsedValue as JSONObjectValue).jsonObject).containsEntry( - "id", - StringValue("abc123") - ) - assertThat(request.formFields).hasSize(1) - return HttpResponse.OK - } - - override fun setServerState(serverState: Map) { - } - }, DefaultStrategies).first + it.runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.formFields).containsKey("Data") + + var parsedValue: Value = JSONObjectValue() + assertThatCode { + parsedValue = parsedJSON(request.formFields["Data"]!!) + }.doesNotThrowAnyException() + + assertThat((parsedValue as JSONObjectValue).jsonObject).containsEntry( + "id", + StringValue("abc123") + ) + assertThat(request.formFields).hasSize(1) + return HttpResponse.OK + } + + override fun setServerState(serverState: Map) { + } + }).first } assertThat(results).hasSize(1) @@ -5575,18 +5575,18 @@ paths: val results: List = feature.generateContractTests(emptyList()).toList().map { - executeTestAndReturnResultAndResponse(it, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.formFields).containsKey("Data") - assertThat(request.formFields["Data"]).isEqualTo("abc123") - assertThat(request.formFields).hasSize(1) - - return HttpResponse.OK - } - - override fun setServerState(serverState: Map) { - } - }, DefaultStrategies).first + it.runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.formFields).containsKey("Data") + assertThat(request.formFields["Data"]).isEqualTo("abc123") + assertThat(request.formFields).hasSize(1) + + return HttpResponse.OK + } + + override fun setServerState(serverState: Map) { + } + }).first } assertThat(results).hasSize(1) @@ -5635,20 +5635,20 @@ paths: val results: List = feature.generateContractTests(emptyList()).toList().map { - executeTestAndReturnResultAndResponse(it, object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - assertThat(request.multiPartFormData.first().name).isEqualTo("Data") + it.runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + assertThat(request.multiPartFormData.first().name).isEqualTo("Data") - val content = request.multiPartFormData.first() as MultiPartContentValue - assertThat(content.content.toStringLiteral()).isEqualTo("abc123") + val content = request.multiPartFormData.first() as MultiPartContentValue + assertThat(content.content.toStringLiteral()).isEqualTo("abc123") - assertThat(request.multiPartFormData).hasSize(1) - return HttpResponse.OK - } + assertThat(request.multiPartFormData).hasSize(1) + return HttpResponse.OK + } - override fun setServerState(serverState: Map) { - } - }, DefaultStrategies).first + override fun setServerState(serverState: Map) { + } + }).first } assertThat(results).hasSize(1) diff --git a/core/src/test/kotlin/in/specmatic/core/ScenarioTest.kt b/core/src/test/kotlin/in/specmatic/core/ScenarioAsTest.kt similarity index 97% rename from core/src/test/kotlin/in/specmatic/core/ScenarioTest.kt rename to core/src/test/kotlin/in/specmatic/core/ScenarioAsTest.kt index 8d399fd69..03323e398 100644 --- a/core/src/test/kotlin/in/specmatic/core/ScenarioTest.kt +++ b/core/src/test/kotlin/in/specmatic/core/ScenarioAsTest.kt @@ -14,7 +14,7 @@ import org.junit.jupiter.api.Test import java.util.* import java.util.function.Consumer -internal class ScenarioTest { +internal class ScenarioAsTest { @Test fun `should generate one test scenario when there are no examples`() { val scenario = Scenario( @@ -516,14 +516,14 @@ paths: val contractTestScenarios = contract.generateContractTests(emptyList()) val result: Result = - executeTestAndReturnResultAndResponse(contractTestScenarios.first(), object : TestExecutor { - override fun execute(request: HttpRequest): HttpResponse { - return HttpResponse.ok("abc") - } - - override fun setServerState(serverState: Map) { - } - }, DefaultStrategies).first as Result.Failure + contractTestScenarios.first().runTest(object : TestExecutor { + override fun execute(request: HttpRequest): HttpResponse { + return HttpResponse.ok("abc") + } + + override fun setServerState(serverState: Map) { + } + }).first as Result.Failure assertThat(result.reportString()).contains("Contract expected") assertThat(result.reportString()).contains("response contained") From 130cfb5db14b6b3efa8cc5a1dae1e30e819894d3 Mon Sep 17 00:00:00 2001 From: Joel Rosario Date: Sun, 30 Jun 2024 16:31:37 +0530 Subject: [PATCH 06/10] Added validator hook and renamed some classes --- .../main/kotlin/in/specmatic/test/ContractTest.kt | 7 +++++++ .../kotlin/in/specmatic/test/ScenarioAsTest.kt | 14 +++++++++++--- .../test/ScenarioTestGenerationException.kt | 4 ++++ .../test/ScenarioTestGenerationFailure.kt | 4 ++++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/core/src/main/kotlin/in/specmatic/test/ContractTest.kt b/core/src/main/kotlin/in/specmatic/test/ContractTest.kt index 6c8baba7c..6924018c7 100644 --- a/core/src/main/kotlin/in/specmatic/test/ContractTest.kt +++ b/core/src/main/kotlin/in/specmatic/test/ContractTest.kt @@ -2,10 +2,17 @@ package `in`.specmatic.test import `in`.specmatic.core.HttpResponse import `in`.specmatic.core.Result +import `in`.specmatic.core.Scenario + +interface ResponseValidator { + fun validate(scenario: Scenario, httpResponse: HttpResponse): Result? +} interface ContractTest { fun testResultRecord(result: Result, response: HttpResponse?): TestResultRecord? fun testDescription(): String fun runTest(testBaseURL: String, timeOut: Int): Pair fun runTest(testExecutor: TestExecutor): Pair + + fun plusValidator(validator: ResponseValidator): ContractTest } diff --git a/core/src/main/kotlin/in/specmatic/test/ScenarioAsTest.kt b/core/src/main/kotlin/in/specmatic/test/ScenarioAsTest.kt index e7f219861..771d3420b 100644 --- a/core/src/main/kotlin/in/specmatic/test/ScenarioAsTest.kt +++ b/core/src/main/kotlin/in/specmatic/test/ScenarioAsTest.kt @@ -8,7 +8,7 @@ import `in`.specmatic.core.log.logger import `in`.specmatic.core.utilities.exceptionCauseMessage import `in`.specmatic.core.value.JSONObjectValue -class ScenarioAsTest( +data class ScenarioAsTest( val scenario: Scenario, private val flagsBased: FlagsBased, private val sourceProvider: String? = null, @@ -16,7 +16,8 @@ class ScenarioAsTest( private val sourceRepositoryBranch: String? = null, private val specification: String? = null, private val serviceType: String? = null, - private val annotations: String? = null + private val annotations: String? = null, + private val validators: List = emptyList() ) : ContractTest { override fun testResultRecord(result: Result, response: HttpResponse?): TestResultRecord { val resultStatus = result.testResult() @@ -55,6 +56,12 @@ class ScenarioAsTest( return Pair(result.updateScenario(scenario), response) } + override fun plusValidator(validator: ResponseValidator): ScenarioAsTest { + return this.copy( + validators = this.validators.plus(validator) + ) + } + private fun logComment() { if (annotations != null) { logger.log(annotations) @@ -75,7 +82,8 @@ class ScenarioAsTest( val response = testExecutor.execute(request) - val result = testResult(request, response, testScenario, flagsBased) + val validatorResult = validators.asSequence().map { it.validate(scenario, response) }.filterNotNull().firstOrNull() + val result = validatorResult ?: testResult(request, response, testScenario, flagsBased) Pair(result.withBindings(testScenario.bindings, response), response) } catch (exception: Throwable) { diff --git a/core/src/main/kotlin/in/specmatic/test/ScenarioTestGenerationException.kt b/core/src/main/kotlin/in/specmatic/test/ScenarioTestGenerationException.kt index 51c52aee1..b6c91ca0f 100644 --- a/core/src/main/kotlin/in/specmatic/test/ScenarioTestGenerationException.kt +++ b/core/src/main/kotlin/in/specmatic/test/ScenarioTestGenerationException.kt @@ -24,6 +24,10 @@ class ScenarioTestGenerationException(val scenario: Scenario, val e: Throwable, return error() } + override fun plusValidator(validator: ResponseValidator): ContractTest { + return this + } + fun error(): Pair { val result: Result = when(e) { is ContractException -> Result.Failure(message, e.failure(), breadCrumb = breadCrumb ?: "").updateScenario(scenario) diff --git a/core/src/main/kotlin/in/specmatic/test/ScenarioTestGenerationFailure.kt b/core/src/main/kotlin/in/specmatic/test/ScenarioTestGenerationFailure.kt index 91fbfbc2c..3434fdb8a 100644 --- a/core/src/main/kotlin/in/specmatic/test/ScenarioTestGenerationFailure.kt +++ b/core/src/main/kotlin/in/specmatic/test/ScenarioTestGenerationFailure.kt @@ -22,4 +22,8 @@ class ScenarioTestGenerationFailure(val scenario: Scenario, val failure: Result. return Pair(failure.updateScenario(scenario), null) } + override fun plusValidator(validator: ResponseValidator): ContractTest { + return this + } + } \ No newline at end of file From 269b3c27221db07ba15324274e5733a7aaf1b40f Mon Sep 17 00:00:00 2001 From: Joel Rosario Date: Sun, 30 Jun 2024 17:31:13 +0530 Subject: [PATCH 07/10] Add descriptive tag to negative test only if description exists --- core/src/main/kotlin/in/specmatic/core/Feature.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/in/specmatic/core/Feature.kt b/core/src/main/kotlin/in/specmatic/core/Feature.kt index b5e28cd61..002ee3c65 100644 --- a/core/src/main/kotlin/in/specmatic/core/Feature.kt +++ b/core/src/main/kotlin/in/specmatic/core/Feature.kt @@ -373,7 +373,11 @@ data class Feature( negativeScenarioResult.ifHasValue { result: HasValue -> val description = result.valueDetails.singleLineDescription() - HasValue(result.value.copy(descriptionFromPlugin = "${result.value.apiDescription} [${description}]")) + val tag = if(description.isNotBlank()) + " [${description}]" + else + "" + HasValue(result.value.copy(descriptionFromPlugin = "${result.value.apiDescription}$tag")) } } From ea0e23c942ab634d7e50bf55bf43ad930656bb17 Mon Sep 17 00:00:00 2001 From: Joel Rosario Date: Sun, 30 Jun 2024 21:43:50 +0530 Subject: [PATCH 08/10] Updated the version to 1.3.32 --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index 89160447b..dc9939ae1 100644 --- a/version.properties +++ b/version.properties @@ -1 +1 @@ -version=1.3.31 +version=1.3.32 From 2732cc822d905d2f14a269ae47aeab133ccf1c07 Mon Sep 17 00:00:00 2001 From: Joel Rosario Date: Mon, 1 Jul 2024 18:14:08 +0530 Subject: [PATCH 09/10] Removed CUSTOM_RESPONSE flag --- .../conversions/EnvironmentAndPropertiesConfiguration.kt | 5 ----- .../main/kotlin/in/specmatic/core/HttpResponsePattern.kt | 7 +------ core/src/main/kotlin/in/specmatic/test/ScenarioAsTest.kt | 8 -------- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/core/src/main/kotlin/in/specmatic/conversions/EnvironmentAndPropertiesConfiguration.kt b/core/src/main/kotlin/in/specmatic/conversions/EnvironmentAndPropertiesConfiguration.kt index 7296c8aed..7eebe3c51 100644 --- a/core/src/main/kotlin/in/specmatic/conversions/EnvironmentAndPropertiesConfiguration.kt +++ b/core/src/main/kotlin/in/specmatic/conversions/EnvironmentAndPropertiesConfiguration.kt @@ -6,7 +6,6 @@ data class EnvironmentAndPropertiesConfiguration(val environmentVariables: Map { - if(Flags.customResponse() && response.status.toString().startsWith("2") && body is JSONObjectValue && body.findFirstChildByPath("resultStatus.status")?.toStringLiteral() == "FAILED") - MatchFailure(mismatchResult("status $status and resultStatus.status == \"SUCCESS\"", "status ${response.status} and resultStatus.status == \"${body.findFirstChildByPath("resultStatus.status")?.toStringLiteral()}\"").copy(breadCrumb = "STATUS", failureReason = FailureReason.StatusMismatch)) - else - MatchSuccess(parameters) - } + status -> MatchSuccess(parameters) else -> MatchFailure(mismatchResult("status $status", "status ${response.status}").copy(breadCrumb = "STATUS", failureReason = FailureReason.StatusMismatch)) } } diff --git a/core/src/main/kotlin/in/specmatic/test/ScenarioAsTest.kt b/core/src/main/kotlin/in/specmatic/test/ScenarioAsTest.kt index 771d3420b..6433e026a 100644 --- a/core/src/main/kotlin/in/specmatic/test/ScenarioAsTest.kt +++ b/core/src/main/kotlin/in/specmatic/test/ScenarioAsTest.kt @@ -6,7 +6,6 @@ import `in`.specmatic.core.log.HttpLogMessage import `in`.specmatic.core.log.LogMessage import `in`.specmatic.core.log.logger import `in`.specmatic.core.utilities.exceptionCauseMessage -import `in`.specmatic.core.value.JSONObjectValue data class ScenarioAsTest( val scenario: Scenario, @@ -103,7 +102,6 @@ data class ScenarioAsTest( val result = when { response.specmaticResultHeaderValue() == "failure" -> Result.Failure(response.body.toStringLiteral()) .updateScenario(testScenario) - response.body is JSONObjectValue && ignorable(response.body) -> Result.Success() else -> testScenario.matches(request, response, ContractAndResponseMismatch, flagsBased?.unexpectedKeyCheck ?: ValidateUnexpectedKeys) } @@ -115,12 +113,6 @@ data class ScenarioAsTest( return result } - fun ignorable(body: JSONObjectValue): Boolean { - return Flags.customResponse() && - (body.findFirstChildByPath("resultStatus.status")?.toStringLiteral() == "FAILED" && - (body.findFirstChildByPath("resultStatus.errorCode")?.toStringLiteral() == "INVALID_REQUEST")) - } - } private fun LogMessage.withComment(comment: String?): LogMessage { From d399decb54ae9156228efc4a3f44a7e2f5defc46 Mon Sep 17 00:00:00 2001 From: Joel Rosario Date: Mon, 1 Jul 2024 18:41:36 +0530 Subject: [PATCH 10/10] Updated the version --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index dc9939ae1..36a3f2764 100644 --- a/version.properties +++ b/version.properties @@ -1 +1 @@ -version=1.3.32 +version=1.3.33