Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functional tests to cover proxy cache host parameter #57

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions src/test/kotlin/org/prebid/cache/functional/ProxyCacheHostSpec.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package org.prebid.cache.functional

import io.kotest.assertions.assertSoftly
import io.kotest.assertions.throwables.shouldThrowExactly
import io.kotest.core.spec.style.ShouldSpec
import io.kotest.data.forAll
import io.kotest.data.row
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain
import io.ktor.client.statement.readText
import io.ktor.http.contentType
import org.mockserver.model.MediaType.APPLICATION_JSON_UTF_8
import org.mockserver.model.MediaType.APPLICATION_XML
import org.prebid.cache.functional.mapper.objectMapper
import org.prebid.cache.functional.model.request.MediaType.JSON
import org.prebid.cache.functional.model.request.MediaType.XML
import org.prebid.cache.functional.model.request.TransferValue
import org.prebid.cache.functional.service.ApiException
import org.prebid.cache.functional.service.PrebidCacheApi
import org.prebid.cache.functional.testcontainers.ContainerDependencies
import org.prebid.cache.functional.testcontainers.client.WebCacheContainerClient
import org.prebid.cache.functional.testcontainers.container.WebCacheContainer
import org.prebid.cache.functional.util.getRandomString
import org.prebid.cache.functional.util.getRandomUuid
import org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR
import org.springframework.http.HttpStatus.NOT_FOUND
import org.springframework.http.HttpStatus.OK

class ProxyCacheHostSpec : ShouldSpec({

lateinit var webCacheContainerClient: WebCacheContainerClient
lateinit var proxyCacheHost: String
lateinit var specPrebidCacheConfig: Map<String, String>
lateinit var prebidCacheApi: PrebidCacheApi

beforeSpec {
// given: Mock web cache container is started
ContainerDependencies.webCacheContainer.start()

// and: Mock web cache client is initialized
webCacheContainerClient = WebCacheContainerClient(
ContainerDependencies.webCacheContainer.host,
ContainerDependencies.webCacheContainer.serverPort
)
// and: Prebid Cache with allow_external_UUID=true and configured proxy cache host is started
proxyCacheHost =
"${ContainerDependencies.webCacheContainer.getContainerHost()}:${WebCacheContainer.PORT}"
specPrebidCacheConfig = BaseSpec.prebidCacheConfig.getBaseRedisConfig("true") +
BaseSpec.prebidCacheConfig.getProxyCacheHostConfig(proxyCacheHost)
prebidCacheApi = BaseSpec.getPrebidCacheApi(specPrebidCacheConfig)
}

afterSpec {
// cleanup: Mock web cache container is stopped
ContainerDependencies.webCacheContainer.stop()

// and: Prebid Cache container is stopped
ContainerDependencies.prebidCacheContainerPool.stopPrebidCacheContainer(specPrebidCacheConfig)
}

should("throw an exception when proxy cache host doesn't exist") {
// given: Prebid cache config with set up not existing proxy cache host
val cacheHost = getRandomString()
val config = BaseSpec.prebidCacheConfig.getBaseRedisConfig("true") +
BaseSpec.prebidCacheConfig.getProxyCacheHostConfig(cacheHost)

// when: GET cache endpoint is called with provided cache host
val exception =
shouldThrowExactly<ApiException> { BaseSpec.getPrebidCacheApi(config).getCache(getRandomUuid(), cacheHost) }

// then: Internal Server Error exception is thrown
assertSoftly {
exception.statusCode shouldBe INTERNAL_SERVER_ERROR.value()
exception.responseBody shouldContain "\"message\":\"$cacheHost: Name or service not known\""
}
}

should("throw an exception when proxy cache host returns not OK status code") {
forAll(
row(NOT_FOUND),
row(INTERNAL_SERVER_ERROR)
) { httpStatus ->
// given: Proxy cache host response is set up to return bad status code and error message
webCacheContainerClient.setProxyCacheHostResponse(httpStatus, httpStatus.name)

// when: GET cache endpoint is called with provided proxy cache host
val exception =
shouldThrowExactly<ApiException> { prebidCacheApi.getCache(getRandomUuid(), proxyCacheHost) }

// then: Exception thrown by proxy cache host is returned by PBC
assertSoftly {
exception.statusCode shouldBe httpStatus.value()
exception.responseBody shouldContain httpStatus.name
}
}
}

should("send a request to proxy cache host when proxy cache host parameter is given on request") {
// given: Proxy cache host response is set up
webCacheContainerClient.setProxyCacheHostResponse(OK, getRandomString())

// and: Initial proxy cache host request count is taken
val initialProxyCacheHostRequestCount = webCacheContainerClient.getProxyCacheHostRecordedRequestCount()

// when: GET cache endpoint is called with provided proxy cache host
val requestUuid = getRandomUuid()
prebidCacheApi.getCache(requestUuid, proxyCacheHost)

// then: PBC called proxy cache host
webCacheContainerClient.getProxyCacheHostRecordedRequestCount() shouldBe initialProxyCacheHostRequestCount + 1

val proxyCacheHostRequest = webCacheContainerClient.getProxyCacheHostRecordedRequests()!!.last()
proxyCacheHostRequest.queryStringParameters?.containsEntry("uuid", requestUuid)
}

should("return a response body as a plain String requested from proxy cache host") {
// given: Proxy cache host response is set up to return a plain String response body
val cacheHostResponseBody = getRandomString()
webCacheContainerClient.setProxyCacheHostResponse(OK, cacheHostResponseBody)

// when: GET cache endpoint is called with provided proxy cache host
val response = prebidCacheApi.getCache(getRandomUuid(), proxyCacheHost)

// then: PBC response body should be equal to proxy cache host response body
response.readText() shouldBe cacheHostResponseBody
}

should("return a response body as a JSON or XML object requested from proxy cache host") {
forAll(
row(TransferValue.getDefaultJsonValue(), APPLICATION_JSON_UTF_8, JSON),
row(TransferValue.getDefaultXmlValue(), APPLICATION_XML, XML)
) { proxyCacheResponseBody, proxyCacheResponseMediaType, prebidCacheResponseMediaType ->
// given: Proxy cache host response is set up to return a JSON response body
webCacheContainerClient.setProxyCacheHostResponse(
OK,
objectMapper.writeValueAsString(proxyCacheResponseBody),
proxyCacheResponseMediaType
)

// when: GET cache endpoint is called with provided proxy cache host
val response = prebidCacheApi.getCache(getRandomUuid(), proxyCacheHost)

// then: PBC response body should be equal to proxy cache host response body
response.contentType()?.contentType shouldBe "application"
response.contentType()?.contentSubtype shouldBe prebidCacheResponseMediaType.getValue()

// and: transfer value is returned
val responseTransferValue = objectMapper.readValue(response.readText(), TransferValue::class.java)

assertSoftly {
responseTransferValue.adm shouldBe proxyCacheResponseBody.adm
responseTransferValue.width shouldBe proxyCacheResponseBody.width
responseTransferValue.height shouldBe proxyCacheResponseBody.height
}
}
}
})
17 changes: 11 additions & 6 deletions src/test/kotlin/org/prebid/cache/functional/SecondaryCacheSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class SecondaryCacheSpec : ShouldSpec({
webCacheContainerClient = WebCacheContainerClient(
ContainerDependencies.webCacheContainer.host,
ContainerDependencies.webCacheContainer.serverPort
)
).apply { initSecondaryCacheResponse() }
webCacheContainerUri =
"http://${ContainerDependencies.webCacheContainer.getContainerHost()}:${WebCacheContainer.PORT}"

Expand Down Expand Up @@ -57,7 +57,8 @@ class SecondaryCacheSpec : ShouldSpec({
responseObject.responses[0].uuid shouldBe requestObject.puts[0].key

// and: Request to secondary cache was sent
val secondaryCacheRecordedRequests = webCacheContainerClient.getRecordedRequests(requestObject.puts[0].key!!)
val secondaryCacheRecordedRequests =
webCacheContainerClient.getSecondaryCacheRecordedRequests(requestObject.puts[0].key!!)
secondaryCacheRecordedRequests?.size shouldBe 1

// and: Request contained secondaryCache=yes query parameter
Expand All @@ -84,7 +85,8 @@ class SecondaryCacheSpec : ShouldSpec({
responseObject.responses[0].uuid shouldBe requestObject.puts[0].key

// and: Request to secondary cache was sent
val secondaryCacheRecordedRequests = webCacheContainerClient.getRecordedRequests(requestObject.puts[0].key!!)
val secondaryCacheRecordedRequests =
webCacheContainerClient.getSecondaryCacheRecordedRequests(requestObject.puts[0].key!!)
secondaryCacheRecordedRequests?.size shouldBe 1

// and: Secondary cache request 'expiry' parameter matches to the PBC request 'ttlseconds' parameter
Expand All @@ -109,7 +111,8 @@ class SecondaryCacheSpec : ShouldSpec({
responseObject.responses[0].uuid shouldBe requestObject.puts[0].key

// and: Request to secondary cache was sent
val secondaryCacheRecordedRequests = webCacheContainerClient.getRecordedRequests(requestObject.puts[0].key!!)
val secondaryCacheRecordedRequests =
webCacheContainerClient.getSecondaryCacheRecordedRequests(requestObject.puts[0].key!!)
secondaryCacheRecordedRequests?.size shouldBe 1

// and: Secondary cache request 'expiry' parameter matches to the Prebid Cache 'cache.expiry.sec' config property
Expand Down Expand Up @@ -137,7 +140,8 @@ class SecondaryCacheSpec : ShouldSpec({
responseObject.responses[0].uuid shouldBe requestObject.puts[0].key

// and: Request to secondary cache was sent
val secondaryCacheRecordedRequests = webCacheContainerClient.getRecordedRequests(requestObject.puts[0].key!!)
val secondaryCacheRecordedRequests =
webCacheContainerClient.getSecondaryCacheRecordedRequests(requestObject.puts[0].key!!)
secondaryCacheRecordedRequests?.size shouldBe 1

// and: Secondary cache request 'expiry' parameter matches to the Prebid Cache 'cache.max.expiry' config property
Expand Down Expand Up @@ -165,7 +169,8 @@ class SecondaryCacheSpec : ShouldSpec({
responseObject.responses[0].uuid shouldBe requestObject.puts[0].key

// and: Request to secondary cache was sent
val secondaryCacheRecordedRequests = webCacheContainerClient.getRecordedRequests(requestObject.puts[0].key!!)
val secondaryCacheRecordedRequests =
webCacheContainerClient.getSecondaryCacheRecordedRequests(requestObject.puts[0].key!!)
secondaryCacheRecordedRequests?.size shouldBe 1

// and: Secondary cache request 'expiry' parameter matches to the Prebid Cache 'cache.min.expiry' config property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ class PrebidCacheApi(prebidCacheHost: String, prebidCachePort: Int) {
}
}

suspend fun getCache(uuid: String?): HttpResponse =
suspend fun getCache(uuid: String?, proxyCacheHost: String? = null): HttpResponse =
client.get(CACHE_ENDPOINT) {
if (uuid != null) parameter(UUID_QUERY_PARAMETER, uuid)
if (proxyCacheHost != null) parameter(PROXY_CACHE_HOST_QUERY_PARAMETER, proxyCacheHost)
}

suspend fun postCache(requestObject: RequestObject, secondaryCache: String? = null): ResponseObject =
Expand All @@ -64,6 +65,7 @@ class PrebidCacheApi(prebidCacheHost: String, prebidCachePort: Int) {
companion object {
private const val CACHE_ENDPOINT = "/cache"
private const val UUID_QUERY_PARAMETER = "uuid"
private const val PROXY_CACHE_HOST_QUERY_PARAMETER = "ch"
private const val SECONDARY_CACHE_QUERY_PARAMETER = "secondaryCache"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ class PrebidCacheContainerConfig(private val redisHost: String, private val aero
"cache.secondary-uris" to secondaryCacheUri
)

fun getProxyCacheHostConfig(cacheHost: String): Map<String, String> =
mapOf(
"cache.host_param_protocol" to "http",
"cache.allowed-proxy-host" to cacheHost
)

private fun getBaseConfig(allowExternalUuid: String): Map<String, String> =
getCachePrefixConfig() + getCacheExpiryConfig() + getAllowExternalUuidConfig(allowExternalUuid) +
getCacheTimeoutConfig("500")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package org.prebid.cache.functional.testcontainers.client

import org.mockserver.client.MockServerClient
import org.mockserver.matchers.Times
import org.mockserver.mock.Expectation
import org.mockserver.model.HttpRequest
import org.mockserver.model.HttpRequest.request
import org.mockserver.model.HttpResponse.response
import org.mockserver.model.JsonPathBody.jsonPath
import org.mockserver.model.MediaType
import org.mockserver.model.MediaType.APPLICATION_JSON_UTF_8
import org.prebid.cache.functional.testcontainers.container.WebCacheContainer.Companion.WEB_CACHE_PATH
import org.springframework.http.HttpMethod.GET
import org.springframework.http.HttpMethod.POST
import org.springframework.http.HttpStatus
import org.springframework.http.HttpStatus.OK

class WebCacheContainerClient(mockServerHost: String, mockServerPort: Int) {
Expand All @@ -16,12 +21,36 @@ class WebCacheContainerClient(mockServerHost: String, mockServerPort: Int) {

init {
mockServerClient = MockServerClient(mockServerHost, mockServerPort)
initResponse()
}

fun getRecordedRequests(uuidKey: String): Array<out HttpRequest>? =
fun getProxyCacheHostRecordedRequests(): Array<out HttpRequest>? =
mockServerClient.retrieveRecordedRequests(getProxyCacheHostRequest())

fun getProxyCacheHostRecordedRequestCount(): Int =
getProxyCacheHostRecordedRequests()?.size ?: 0

fun setProxyCacheHostResponse(
httpStatus: HttpStatus = OK,
body: String = "",
mediaType: MediaType = APPLICATION_JSON_UTF_8
): Array<out Expectation>? =
mockServerClient.`when`(getProxyCacheHostRequest(), Times.exactly(1))
.respond(
response().withStatusCode(httpStatus.value())
.withBody(body, mediaType)
)

fun getSecondaryCacheRecordedRequests(uuidKey: String): Array<out HttpRequest>? =
mockServerClient.retrieveRecordedRequests(getSecondaryCacheRequest(uuidKey))

fun initSecondaryCacheResponse(): Array<out Expectation>? =
mockServerClient.`when`(getSecondaryCacheRequest())
.respond(response().withStatusCode(OK.value()))

private fun getProxyCacheHostRequest(): HttpRequest =
request().withMethod(GET.name)
.withPath("/$WEB_CACHE_PATH")

private fun getSecondaryCacheRequest(): HttpRequest =
request().withMethod(POST.name)
.withPath("/$WEB_CACHE_PATH")
Expand All @@ -30,8 +59,4 @@ class WebCacheContainerClient(mockServerHost: String, mockServerPort: Int) {
request().withMethod(POST.name)
.withPath("/$WEB_CACHE_PATH")
.withBody(jsonPath("\$.puts[?(@.key == '$uuidKey')]"))

private fun initResponse(): Array<out Expectation>? =
mockServerClient.`when`(getSecondaryCacheRequest())
.respond(response().withStatusCode(OK.value()))
}