Skip to content
Open
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
13 changes: 0 additions & 13 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@
<kotlin.version>2.4.0</kotlin.version>
<kotlinx-coroutines-core-jvm.version>1.11.0</kotlinx-coroutines-core-jvm.version>
<testcontainers.version>1.21.4</testcontainers.version>
<mockserver-client.version>5.15.0</mockserver-client.version>
<ktor.version>2.3.13</ktor.version>
<jackson-kotlin-module.version>2.16.1</jackson-kotlin-module.version>
<docker-maven-plugin.version>0.44.0</docker-maven-plugin.version>
Expand Down Expand Up @@ -213,18 +212,6 @@
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mockserver</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-client-java</artifactId>
<version>${mockserver-client.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-client-core-jvm</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain
import io.ktor.client.statement.bodyAsText
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
Expand All @@ -25,6 +23,7 @@ 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
import org.springframework.http.MediaType

class ProxyCacheHostSpec : ShouldSpec({

Expand Down Expand Up @@ -109,8 +108,8 @@ class ProxyCacheHostSpec : ShouldSpec({
// then: PBC called proxy cache host
webCacheContainerClient.getProxyCacheHostRecordedRequestCount() shouldBe initialProxyCacheHostRequestCount + 1

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

should("return a response body as a plain String requested from proxy cache host") {
Expand All @@ -127,8 +126,8 @@ class ProxyCacheHostSpec : ShouldSpec({

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)
row(TransferValue.getDefaultJsonValue(), MediaType.APPLICATION_JSON, JSON),
row(TransferValue.getDefaultXmlValue(), MediaType.APPLICATION_XML, XML)
) { proxyCacheResponseBody, proxyCacheResponseMediaType, prebidCacheResponseMediaType ->
// given: Proxy cache host response is set up to return a JSON response body
webCacheContainerClient.setProxyCacheHostResponse(
Expand Down
22 changes: 11 additions & 11 deletions src/test/kotlin/org/prebid/cache/functional/SecondaryCacheSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ class SecondaryCacheSpec : ShouldSpec({
// and: Request to secondary cache was sent
val secondaryCacheRecordedRequests =
webCacheContainerClient.getSecondaryCacheRecordedRequests(requestObject.puts[0].key!!)
secondaryCacheRecordedRequests?.size shouldBe 1
secondaryCacheRecordedRequests.size shouldBe 1

// and: Request contained secondaryCache=yes query parameter
secondaryCacheRecordedRequests!!.first().queryStringParameters
?.containsEntry("secondaryCache", "yes") shouldBe true
secondaryCacheRecordedRequests.first()
.queryParameter("secondaryCache").containsValue("yes") shouldBe true

// and: Secondary cache request body matched to the Prebid Cache request object
val secondaryCacheRequest =
Expand All @@ -88,11 +88,11 @@ class SecondaryCacheSpec : ShouldSpec({
// and: Request to secondary cache was sent
val secondaryCacheRecordedRequests =
webCacheContainerClient.getSecondaryCacheRecordedRequests(requestObject.puts[0].key!!)
secondaryCacheRecordedRequests?.size shouldBe 1
secondaryCacheRecordedRequests.size shouldBe 1

// and: Secondary cache request 'expiry' parameter matches to the PBC request 'ttlseconds' parameter
val secondaryCacheRequest =
objectMapper.readValue(secondaryCacheRecordedRequests!!.first().bodyAsString, RequestObject::class.java)
objectMapper.readValue(secondaryCacheRecordedRequests.first().bodyAsString, RequestObject::class.java)
secondaryCacheRequest.puts.size shouldBe 1
secondaryCacheRequest.puts[0].expiry shouldBe requestObject.puts[0].ttlseconds
}
Expand All @@ -115,11 +115,11 @@ class SecondaryCacheSpec : ShouldSpec({
// and: Request to secondary cache was sent
val secondaryCacheRecordedRequests =
webCacheContainerClient.getSecondaryCacheRecordedRequests(requestObject.puts[0].key!!)
secondaryCacheRecordedRequests?.size shouldBe 1
secondaryCacheRecordedRequests.size shouldBe 1

// and: Secondary cache request 'expiry' parameter matches to the Prebid Cache 'cache.expiry.sec' config property
val secondaryCacheRequest =
objectMapper.readValue(secondaryCacheRecordedRequests!!.first().bodyAsString, RequestObject::class.java)
objectMapper.readValue(secondaryCacheRecordedRequests.first().bodyAsString, RequestObject::class.java)
secondaryCacheRequest.puts.size shouldBe 1
secondaryCacheRequest.puts[0].expiry shouldBe specPrebidCacheConfig["cache.expiry.sec"]?.toLong()
}
Expand All @@ -145,11 +145,11 @@ class SecondaryCacheSpec : ShouldSpec({
// and: Request to secondary cache was sent
val secondaryCacheRecordedRequests =
webCacheContainerClient.getSecondaryCacheRecordedRequests(requestObject.puts[0].key!!)
secondaryCacheRecordedRequests?.size shouldBe 1
secondaryCacheRecordedRequests.size shouldBe 1

// and: Secondary cache request 'expiry' parameter matches to the Prebid Cache 'cache.max.expiry' config property
val secondaryCacheRequest =
objectMapper.readValue(secondaryCacheRecordedRequests!!.first().bodyAsString, RequestObject::class.java)
objectMapper.readValue(secondaryCacheRecordedRequests.first().bodyAsString, RequestObject::class.java)
secondaryCacheRequest.puts.size shouldBe 1
secondaryCacheRequest.puts[0].expiry shouldBe configCacheMaxExpiry
}
Expand All @@ -175,11 +175,11 @@ class SecondaryCacheSpec : ShouldSpec({
// and: Request to secondary cache was sent
val secondaryCacheRecordedRequests =
webCacheContainerClient.getSecondaryCacheRecordedRequests(requestObject.puts[0].key!!)
secondaryCacheRecordedRequests?.size shouldBe 1
secondaryCacheRecordedRequests.size shouldBe 1

// and: Secondary cache request 'expiry' parameter matches to the Prebid Cache 'cache.min.expiry' config property
val secondaryCacheRequest =
objectMapper.readValue(secondaryCacheRecordedRequests!!.first().bodyAsString, RequestObject::class.java)
objectMapper.readValue(secondaryCacheRecordedRequests.first().bodyAsString, RequestObject::class.java)
secondaryCacheRequest.puts.size shouldBe 1
secondaryCacheRequest.puts[0].expiry shouldBe configCacheMinExpiry
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ abstract class ContainerDependencies {
private const val aerospikeImageName = "aerospike:ce-8.1.2.2"
private const val apacheIgniteImageName = "apacheignite/ignite:2.18.0"
private const val prebidCacheImageName = "prebid-cache:latest"
private const val mockServerImageVersion = "mockserver/mockserver:5.15.0"
private const val wireMockImageVersion = "wiremock/wiremock:3.13.0"

private val launchContainers = System.getProperty("launchContainers")?.toBoolean() ?: true

Expand All @@ -24,7 +24,7 @@ abstract class ContainerDependencies {
val aerospikeContainer: AerospikeContainer = AerospikeContainer(aerospikeImageName).withNetwork(network)
val apacheIgniteContainer: ApacheIgniteContainer = ApacheIgniteContainer(apacheIgniteImageName).withNetwork(network)
val webCacheContainer: WebCacheContainer =
WebCacheContainer(DockerImageName.parse(mockServerImageVersion)).withNetwork(network) as WebCacheContainer
WebCacheContainer(DockerImageName.parse(wireMockImageVersion)).withNetwork(network) as WebCacheContainer
val prebidCacheContainerPool = PrebidCacheContainerPool(prebidCacheImageName)

fun startCacheServerContainers() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,67 +1,80 @@
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 com.github.tomakehurst.wiremock.client.WireMock
import com.github.tomakehurst.wiremock.client.WireMock.aResponse
import com.github.tomakehurst.wiremock.client.WireMock.get
import com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor
import com.github.tomakehurst.wiremock.client.WireMock.matchingJsonPath
import com.github.tomakehurst.wiremock.client.WireMock.post
import com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor
import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo
import com.github.tomakehurst.wiremock.stubbing.Scenario
import com.github.tomakehurst.wiremock.stubbing.StubMapping
import com.github.tomakehurst.wiremock.verification.LoggedRequest
import java.util.UUID
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
import org.springframework.http.MediaType

class WebCacheContainerClient(mockServerHost: String, mockServerPort: Int) {

private val mockServerClient: MockServerClient
private val wireMockClient = WireMock(mockServerHost, mockServerPort)
private var proxyCacheHostStub: StubMapping? = null

init {
mockServerClient = MockServerClient(mockServerHost, mockServerPort)
}

fun getProxyCacheHostRecordedRequests(): Array<out HttpRequest>? =
mockServerClient.retrieveRecordedRequests(getProxyCacheHostRequest())
fun getProxyCacheHostRecordedRequests(): List<LoggedRequest> =
wireMockClient.find(getProxyCacheHostRequest())

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

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)
)
body: String = "{}",
mediaType: MediaType = MediaType.APPLICATION_JSON
): StubMapping {
proxyCacheHostStub?.let { wireMockClient.removeStubMapping(it) }

return wireMockClient.register(
get(urlPathEqualTo("/$WEB_CACHE_PATH"))
.inScenario("proxy-cache-host-${UUID.randomUUID()}")
.whenScenarioStateIs(Scenario.STARTED)
.willReturn(
aResponse()
.withStatus(httpStatus.value())
.withBody(body)
.withHeader("Content-Type", mediaType.toString())
.withHeader("Connection", "close")
)
.willSetStateTo("CONSUMED")
).also { proxyCacheHostStub = it }
}

fun getSecondaryCacheRecordedRequests(uuidKey: String): Array<out HttpRequest>? {
fun getSecondaryCacheRecordedRequests(uuidKey: String): List<LoggedRequest> {
val secondaryCacheRequest = getSecondaryCacheRequest(uuidKey)
waitUntil({ mockServerClient.retrieveRecordedRequests(secondaryCacheRequest)!!.isNotEmpty() })
return mockServerClient.retrieveRecordedRequests(secondaryCacheRequest)
waitUntil({ wireMockClient.find(secondaryCacheRequest).isNotEmpty() })
return wireMockClient.find(secondaryCacheRequest)
}

fun initSecondaryCacheResponse(): Array<out Expectation>? =
mockServerClient.`when`(getSecondaryCacheRequest())
.respond(response().withStatusCode(OK.value()))
fun initSecondaryCacheResponse(): StubMapping =
wireMockClient.register(
post(urlPathEqualTo("/$WEB_CACHE_PATH"))
.willReturn(
aResponse()
.withStatus(OK.value())
.withHeader("Connection", "close")
)
)

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

private fun getSecondaryCacheRequest(): HttpRequest =
request().withMethod(POST.name())
.withPath("/$WEB_CACHE_PATH")
private fun getSecondaryCacheRequest() =
postRequestedFor(urlPathEqualTo("/$WEB_CACHE_PATH"))

private fun getSecondaryCacheRequest(uuidKey: String): HttpRequest =
request().withMethod(POST.name())
.withPath("/$WEB_CACHE_PATH")
.withBody(jsonPath("\$.puts[?(@.key == '$uuidKey')]"))
private fun getSecondaryCacheRequest(uuidKey: String) =
getSecondaryCacheRequest()
.withRequestBody(matchingJsonPath("$.puts[?(@.key == '$uuidKey')]"))

private fun waitUntil(closure: () -> Boolean, timeoutMs: Long = 5000, pollInterval: Long = 100) {
val startTime = System.currentTimeMillis()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
package org.prebid.cache.functional.testcontainers.container

import org.testcontainers.containers.MockServerContainer
import org.testcontainers.containers.GenericContainer
import org.testcontainers.utility.DockerImageName

class WebCacheContainer(dockerImageName: DockerImageName) : MockServerContainer(dockerImageName) {
class WebCacheContainer(dockerImageName: DockerImageName) : GenericContainer<WebCacheContainer>(dockerImageName) {

fun getContainerHost(): String = networkAliases.first()

init {
withExposedPorts(PORT)
}

val serverPort: Int
get() = getMappedPort(PORT)

companion object {
const val PORT = MockServerContainer.PORT
const val PORT = 8080
const val WEB_CACHE_PATH = "cache"
}
}
Loading