Test openapi schema requestBody
This commit is contained in:
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.node.TextNode
|
||||
import fr.dcproject.common.utils.getResource
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.Url
|
||||
import io.ktor.request.contentType
|
||||
import io.ktor.request.httpMethod
|
||||
@@ -11,14 +12,27 @@ import io.ktor.request.uri
|
||||
import io.ktor.server.testing.TestApplicationResponse
|
||||
import org.openapi4j.core.model.v3.OAI3
|
||||
import org.openapi4j.parser.OpenApi3Parser
|
||||
import org.openapi4j.parser.model.v3.OpenApi3
|
||||
import org.openapi4j.parser.model.v3.Operation
|
||||
import org.openapi4j.parser.model.v3.Schema
|
||||
import org.openapi4j.schema.validator.ValidationContext
|
||||
import org.openapi4j.schema.validator.ValidationData
|
||||
import org.openapi4j.schema.validator.v3.SchemaValidator
|
||||
import java.io.File
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
fun TestApplicationResponse.`And schema must be valid`(route: String? = null) {
|
||||
OpenApi3Parser().parse(File("/openapi2.yaml".getResource().toURI()), true).let { api ->
|
||||
fun Schema.validate(api: OpenApi3, toValidate: JsonNode) {
|
||||
val validationContext: ValidationContext<OAI3> = ValidationContext(api.context)
|
||||
val schemaValidator = SchemaValidator(validationContext, "", this.toNode())
|
||||
|
||||
val results = ValidationData<Unit>()
|
||||
schemaValidator.validate(toValidate, results)
|
||||
|
||||
assertTrue(results.isValid, results.results().toString())
|
||||
}
|
||||
|
||||
fun TestApplicationResponse.operation(route: String? = null, callback: Operation.(OpenApi3, String) -> Unit): Operation {
|
||||
return OpenApi3Parser().parse(File("/openapi2.yaml".getResource().toURI()), true).let { api: OpenApi3 ->
|
||||
val operation = call.request.httpMethod
|
||||
val uri = route ?: "/" + Url(call.request.uri).encodedPath
|
||||
val path = api.paths
|
||||
@@ -28,38 +42,43 @@ fun TestApplicationResponse.`And schema must be valid`(route: String? = null) {
|
||||
api.getPath(path)
|
||||
?.getOperation(operation.value.toLowerCase())
|
||||
?.apply {
|
||||
val mediaType = call.request.contentType()
|
||||
val status = call.response.status()
|
||||
getResponse(status?.value?.toString() ?: error("HttpStatus not found"))
|
||||
?.getContentMediaType(mediaType.toString())
|
||||
?.schema?.let { schema ->
|
||||
val validationContext: ValidationContext<OAI3> = ValidationContext(api.context)
|
||||
val jsonNode: JsonNode = schema.toNode()
|
||||
val schemaValidator = SchemaValidator(validationContext, "", jsonNode)
|
||||
|
||||
val results = ValidationData<Unit>()
|
||||
val mapper = ObjectMapper()
|
||||
schemaValidator.validate(mapper.readTree(content), results)
|
||||
|
||||
assertTrue(results.isValid, results.results().toString())
|
||||
}
|
||||
?: error("""No path found for "$operation $uri" for status ${status.value} with media type "$mediaType".""")
|
||||
}
|
||||
?.apply {
|
||||
Url(call.request.uri).parameters.forEach { parameter: String, values: List<String> ->
|
||||
getParametersIn(api.context, "query")
|
||||
?.firstOrNull { it.name == parameter }?.schema?.let { schema ->
|
||||
val validationContext: ValidationContext<OAI3> = ValidationContext(api.context)
|
||||
val jsonNode: JsonNode = schema.toNode()
|
||||
val schemaValidator = SchemaValidator(validationContext, "", jsonNode)
|
||||
val params = ValidationData<Unit>()
|
||||
schemaValidator.validate(TextNode(values.first()), params)
|
||||
|
||||
assertTrue(params.isValid, params.results().toString())
|
||||
}
|
||||
?: error("""No path found for "$operation $uri" for status "$parameter".""")
|
||||
}
|
||||
this.callback(api, uri)
|
||||
}
|
||||
?: error("""No path found for "$operation $uri".""")
|
||||
}
|
||||
}
|
||||
|
||||
fun TestApplicationResponse.`And the schema must be valid`(route: String? = null, contentType: ContentType? = ContentType.Application.Json) {
|
||||
operation(route) { api, uri ->
|
||||
/* Validate Response */
|
||||
this.apply {
|
||||
val status = call.response.status()
|
||||
getResponse(status?.value?.toString() ?: error("HttpStatus not found"))
|
||||
?.getContentMediaType(contentType.toString())
|
||||
?.schema
|
||||
?.validate(api, ObjectMapper().readTree(content))
|
||||
?: error("""No path found for "$this $uri" for status ${status.value} with media type "$contentType".""")
|
||||
}
|
||||
/* Validate Request URL */
|
||||
this.apply {
|
||||
Url(call.request.uri).parameters.forEach { parameter: String, values: List<String> ->
|
||||
getParametersIn(api.context, "query")
|
||||
?.firstOrNull { it.name == parameter }?.schema
|
||||
?.validate(api, TextNode(values.first()))
|
||||
?: error("""No path found for "$this $uri" for status "$parameter".""")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate request body
|
||||
*/
|
||||
fun TestApplicationResponse.`And the schema request body must be valid`(body: String) {
|
||||
operation { api, uri ->
|
||||
requestBody
|
||||
.getContentMediaType(call.request.contentType().toString())
|
||||
.schema
|
||||
.validate(api, ObjectMapper().readTree(body))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package integration.steps.`when`
|
||||
|
||||
import integration.steps.then.`And the schema must be valid`
|
||||
import integration.steps.then.`And the schema request body must be valid`
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.HttpHeaders
|
||||
import io.ktor.http.HttpMethod
|
||||
@@ -8,7 +11,7 @@ import io.ktor.server.testing.TestApplicationEngine
|
||||
import io.ktor.server.testing.TestApplicationRequest
|
||||
import io.ktor.server.testing.setBody
|
||||
|
||||
fun TestApplicationEngine.`When I send a GET request`(uri: String? = null, setup: (TestApplicationRequest.() -> Unit)? = null): TestApplicationCall {
|
||||
fun TestApplicationEngine.`When I send a GET request`(uri: String? = null, validate: Boolean = true, setup: (TestApplicationRequest.() -> Unit)? = null): TestApplicationCall {
|
||||
return handleRequest(true) {
|
||||
method = HttpMethod.Get
|
||||
if (uri != null) {
|
||||
@@ -16,36 +19,54 @@ fun TestApplicationEngine.`When I send a GET request`(uri: String? = null, setup
|
||||
}
|
||||
addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString())
|
||||
setup?.let { it() }
|
||||
}.apply {
|
||||
if (validate) {
|
||||
response.`And the schema must be valid`()
|
||||
requestBody?.let { body ->
|
||||
response.`And the schema request body must be valid`(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun TestApplicationEngine.`When I send a POST request`(uri: String? = null, setup: (TestApplicationRequest.() -> String?)? = null): TestApplicationCall {
|
||||
fun TestApplicationEngine.`When I send a POST request`(uri: String? = null, validate: Boolean = true, setup: (TestApplicationRequest.() -> Unit)? = null): TestApplicationCall {
|
||||
return handleRequest(true) {
|
||||
method = HttpMethod.Post
|
||||
if (uri != null) {
|
||||
this.uri = uri
|
||||
}
|
||||
addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString())
|
||||
setup?.let { it() }?.let {
|
||||
setBody(it.trimIndent())
|
||||
addHeader(HttpHeaders.Accept, ContentType.Application.Json.toString())
|
||||
setup?.let { it() }
|
||||
}.apply {
|
||||
if (validate) {
|
||||
response.`And the schema must be valid`()
|
||||
requestBody?.let { body ->
|
||||
response.`And the schema request body must be valid`(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun TestApplicationEngine.`When I send a PUT request`(uri: String? = null, setup: (TestApplicationRequest.() -> String?)? = null): TestApplicationCall {
|
||||
fun TestApplicationEngine.`When I send a PUT request`(uri: String? = null, validate: Boolean = true, setup: (TestApplicationRequest.() -> Unit)? = null): TestApplicationCall {
|
||||
return handleRequest(true) {
|
||||
method = HttpMethod.Put
|
||||
if (uri != null) {
|
||||
this.uri = uri
|
||||
}
|
||||
addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString())
|
||||
setup?.let { it() }?.let {
|
||||
setBody(it.trimIndent())
|
||||
setup?.let { it() }
|
||||
}.apply {
|
||||
if (validate) {
|
||||
response.`And the schema must be valid`()
|
||||
requestBody?.let { body ->
|
||||
response.`And the schema request body must be valid`(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun TestApplicationEngine.`When I send a DELETE request`(uri: String? = null, setup: (TestApplicationRequest.() -> String?)? = null): TestApplicationCall {
|
||||
fun TestApplicationEngine.`When I send a DELETE request`(uri: String? = null, validate: Boolean = true, setup: (TestApplicationRequest.() -> String?)? = null): TestApplicationCall {
|
||||
return handleRequest(true) {
|
||||
method = HttpMethod.Delete
|
||||
if (uri != null) {
|
||||
@@ -55,9 +76,30 @@ fun TestApplicationEngine.`When I send a DELETE request`(uri: String? = null, se
|
||||
setup?.let { it() }?.let {
|
||||
setBody(it.trimIndent())
|
||||
}
|
||||
}.apply {
|
||||
if (validate) {
|
||||
response.`And the schema must be valid`()
|
||||
requestBody?.let { body ->
|
||||
response.`And the schema request body must be valid`(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun TestApplicationRequest.`with body`(body: String) {
|
||||
setBody(body.trimIndent())
|
||||
private val requestBodies: MutableMap<ApplicationCall, String> = mutableMapOf()
|
||||
var TestApplicationCall.requestBody: String?
|
||||
get() = requestBodies[this]
|
||||
set(value) {
|
||||
if (value == null) {
|
||||
requestBodies.remove(this)
|
||||
} else {
|
||||
requestBodies[this] = value
|
||||
}
|
||||
}
|
||||
|
||||
infix fun TestApplicationRequest.`with body`(body: String) {
|
||||
return body.trimIndent().let {
|
||||
setBody(it)
|
||||
(call as TestApplicationCall).requestBody = it
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user