All Downloads are FREE. Search and download functionalities are using the official Maven repository.

run.qontract.core.HttpResponsePattern.kt Maven / Gradle / Ivy

Go to download

A Contract Testing Tool that leverages Gherkin to describe APIs in a human readable and machine enforceable manner

There is a newer version: 0.23.1
Show newest version
package run.qontract.core

import run.qontract.core.pattern.*
import run.qontract.core.value.StringValue
import run.qontract.stub.softCastValueToXML

data class HttpResponsePattern(val headersPattern: HttpHeadersPattern = HttpHeadersPattern(), val status: Int = 0, val body: Pattern = EmptyStringPattern) {
    constructor(response: HttpResponse) : this(HttpHeadersPattern(response.headers.mapValues { stringToPattern(it.value, it.key) }), response.status, response.body.exactMatchElseType())

    fun generateResponse(resolver: Resolver): HttpResponse {
        return attempt(breadCrumb = "RESPONSE") {
            val value = softCastValueToXML(body.generate(resolver))
            val headers = headersPattern.generate(resolver).plus(QONTRACT_RESULT_HEADER to "success").let { headers ->
                when {
                    !headers.containsKey("Content-Type") -> headers.plus("Content-Type" to value.httpContentType)
                    else -> headers
                }
            }
            HttpResponse(status, headers, value)
        }
    }

    fun matches(response: HttpResponse, resolver: Resolver): Result {
        val result = response to resolver to
                ::matchStatus then
                ::matchHeaders then
                ::matchBody otherwise
                ::handleError toResult
                ::returnResult

        return when(result) {
            is Result.Failure -> result.breadCrumb("RESPONSE")
            else -> result
        }
    }

    fun newBasedOn(row: Row, resolver: Resolver): List =
        attempt(breadCrumb = "RESPONSE") {
            body.newBasedOn(row, resolver).flatMap { newBody ->
                headersPattern.newBasedOn(row, resolver).map { newHeadersPattern ->
                    HttpResponsePattern(newHeadersPattern, status, newBody)
                }
            }
        }

    fun matchesMock(response: HttpResponse, resolver: Resolver) = matches(response, resolver)

    private fun matchStatus(parameters: Pair): MatchingResult> {
        val (response, _) = parameters
        when (response.status != status) {
            true -> return MatchFailure(Result.Failure(message = "Expected status: $status, actual: ${response.status}", breadCrumb = "STATUS"))
        }
        return MatchSuccess(parameters)
    }

    private fun matchHeaders(parameters: Pair): MatchingResult> {
        val (response, resolver) = parameters
        when (val result = headersPattern.matches(response.headers, resolver)) {
            is Result.Failure -> return MatchFailure(result)
        }
        return MatchSuccess(parameters)
    }

    private fun matchBody(parameters: Pair): MatchingResult> {
        val (response, resolver) = parameters

        val parsedValue = when (response.body) {
            is StringValue -> try { body.parse(response.body.string, resolver) } catch(e: Throwable) { response.body }
            else -> response.body
        }

        when (val result = body.matches(parsedValue, resolver)) {
            is Result.Failure -> return MatchFailure(result.breadCrumb("BODY"))
        }
        return MatchSuccess(parameters)
    }

    fun bodyPattern(newBody: Pattern): HttpResponsePattern = this.copy(body = newBody)

    fun encompasses(other: HttpResponsePattern, olderResolver: Resolver, newerResolver: Resolver): Result {
        val result = listOf(
                {
                    when {
                        status != other.status -> Result.Failure("The status didn't match", breadCrumb = "STATUS")
                        else -> Result.Success()
                    }
                },
                { headersPattern.encompasses(other.headersPattern, Resolver(), Resolver()) },
                { resolvedHop(body, olderResolver).encompasses(resolvedHop(other.body, newerResolver), olderResolver, newerResolver) }
        ).asSequence().map { it.invoke() }.firstOrNull { it is Result.Failure } ?: Result.Success()

        return result.breadCrumb("RESPONSE")
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy