diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 125eafe5d40..3f90c1377d2 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -36,4 +36,4 @@ jobs: ${{ runner.os }}-maven- - name: Build with Maven - run: mvn -B package --file extra/pom.xml + run: mvn -B verify --file extra/pom.xml diff --git a/.gitignore b/.gitignore index 8c65eb25152..49495464430 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ target/ .DS_Store + +.allure/ diff --git a/.maven-dockerinclude b/.maven-dockerinclude new file mode 100644 index 00000000000..4653fc53688 --- /dev/null +++ b/.maven-dockerinclude @@ -0,0 +1,2 @@ +target/*.jar +src/main/docker/* diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000000..ae1d10bc210 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM openjdk:11-jdk-slim + +WORKDIR /app/prebid-server + +VOLUME /app/prebid-server/conf +VOLUME /app/prebid-server/data + +COPY src/main/docker/run.sh ./ +COPY src/main/docker/application.yaml ./ +COPY target/prebid-server.jar ./ + +EXPOSE 8080 +EXPOSE 8060 + +ENTRYPOINT [ "/app/prebid-server/run.sh" ] diff --git a/pom.xml b/pom.xml index a2b9873c30b..9f20528094e 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,11 @@ 9.4.43.v20210629 3.0.6 1.4.196 + 1.3-groovy-2.5 + 1.15.3 + 5.11.2 + 2.14.0 + 1.9.7 3.1.0 @@ -63,7 +68,11 @@ 0.8.2 2.2.4 3.8.0 - 2.22.1 + 2.22.2 + ${maven-surefire-plugin.version} + 0.36.0 + 1.12.1 + 2.10.0 @@ -195,6 +204,18 @@ jackson-module-afterburner ${jackson.version} + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson.version} + test + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + ${jackson.version} + test + com.networknt json-schema-validator @@ -400,6 +421,17 @@ ${restassured.version} test + + org.spockframework + spock-core + ${spock.version} + test + + + org.hibernate + hibernate-core + test + io.vertx @@ -413,6 +445,41 @@ ${h2.version} test + + org.testcontainers + testcontainers + ${testcontainers.version} + test + + + org.testcontainers + spock + ${testcontainers.version} + test + + + org.testcontainers + mockserver + ${testcontainers.version} + test + + + org.testcontainers + mysql + ${testcontainers.version} + test + + + org.mock-server + mockserver-client-java + ${mockserver-client.version} + test + + + io.qameta.allure + allure-spock + ${allure.version} + @@ -429,6 +496,11 @@ maven-surefire-plugin ${maven-surefire-plugin.version} + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + org.apache.maven.plugins maven-checkstyle-plugin @@ -451,6 +523,11 @@ + + org.codehaus.gmavenplus + gmavenplus-plugin + ${gmavenplus-plugin.version} + @@ -576,8 +653,72 @@ + + io.fabric8 + docker-maven-plugin + ${docker-maven-plugin.version} + + + build-containers + pre-integration-test + + build + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + + + + compileTests + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar" + + + target/allure-results + ${mockserver-client.version} + + + + + + integration-test + verify + + + + **/*Spec.java + + + + + + + org.aspectj + aspectjweaver + ${aspectj.version} + + + + + io.qameta.allure + allure-maven + ${allure-maven.version} + + diff --git a/src/main/docker/application.yaml b/src/main/docker/application.yaml new file mode 100644 index 00000000000..458f9b4d25e --- /dev/null +++ b/src/main/docker/application.yaml @@ -0,0 +1,4 @@ +gdpr: + vendorlist: + v2: + cache-dir: /app/prebid-server/data/vendorlist-v2 diff --git a/src/main/docker/run.sh b/src/main/docker/run.sh new file mode 100755 index 00000000000..54f73437643 --- /dev/null +++ b/src/main/docker/run.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +exec java \ + -Dvertx.cacheDirBase=/app/prebid-server/data/.vertx \ + -Dspring.config.additional-location=/app/prebid-server/,/app/prebid-server/conf/ \ + ${JAVA_OPTS} \ + -jar \ + /app/prebid-server/prebid-server.jar diff --git a/src/test/groovy/.editorconfig b/src/test/groovy/.editorconfig new file mode 100644 index 00000000000..63243037b4d --- /dev/null +++ b/src/test/groovy/.editorconfig @@ -0,0 +1,41 @@ +[{*.gant, *.gradle, *.groovy, *.gson, *.gy}] +ij_groovy_align_multiline_chained_methods = true +ij_groovy_array_initializer_wrap = normal +ij_groovy_binary_operation_wrap = normal +ij_groovy_blank_lines_after_class_header = 1 +ij_groovy_blank_lines_after_imports = 1 +ij_groovy_blank_lines_after_package = 1 +ij_groovy_blank_lines_around_class = 1 +ij_groovy_blank_lines_around_field = 0 +ij_groovy_blank_lines_around_field_in_interface = 0 +ij_groovy_blank_lines_around_method = 1 +ij_groovy_blank_lines_around_method_in_interface = 1 +ij_groovy_blank_lines_before_imports = 1 +ij_groovy_blank_lines_before_method_body = 0 +ij_groovy_blank_lines_before_package = 0 +ij_groovy_class_count_to_use_import_on_demand = 999 +ij_groovy_do_while_brace_force = always +ij_groovy_extends_list_wrap = normal +ij_groovy_for_brace_force = always +ij_groovy_for_statement_wrap = normal +ij_groovy_if_brace_force = always +ij_groovy_imports_layout = *, |, java.**, |, $* +ij_groovy_insert_inner_class_imports = true +ij_groovy_keep_blank_lines_before_right_brace = 2 +ij_groovy_keep_blank_lines_in_code = 1 +ij_groovy_keep_blank_lines_in_declarations = 2 +ij_groovy_keep_control_statement_in_one_line = false +ij_groovy_keep_line_breaks = true +ij_groovy_keep_multiple_expressions_in_one_line = false +ij_groovy_keep_simple_blocks_in_one_line = false +ij_groovy_keep_simple_classes_in_one_line = true +ij_groovy_keep_simple_lambdas_in_one_line = true +ij_groovy_keep_simple_methods_in_one_line = true +ij_groovy_layout_static_imports_separately = true +ij_groovy_line_comment_at_first_column = true +ij_groovy_method_parameters_wrap = on_every_item +ij_groovy_names_count_to_use_import_on_demand = 999 +ij_groovy_ternary_operation_wrap = normal +ij_groovy_throws_keyword_wrap = normal +ij_groovy_use_flying_geese_braces = true +ij_groovy_while_brace_force = always diff --git a/src/test/groovy/org/prebid/server/functional/AmpSpec.groovy b/src/test/groovy/org/prebid/server/functional/AmpSpec.groovy new file mode 100644 index 00000000000..a2c15ff86e5 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/AmpSpec.groovy @@ -0,0 +1,124 @@ +package org.prebid.server.functional + +import org.prebid.server.functional.model.db.StoredRequest +import org.prebid.server.functional.model.request.amp.AmpRequest +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.service.PrebidServerService +import org.prebid.server.functional.util.PBSUtils +import spock.lang.Shared +import spock.lang.Unroll + +class AmpSpec extends BaseSpec { + + private static final int MAX_TIMEOUT = 5000 + private static final int DEFAULT_TIMEOUT = PBSUtils.getRandomNumber(0, MAX_TIMEOUT) + + @Shared + PrebidServerService prebidServerService = pbsServiceFactory.getService(["auction.max-timeout-ms" : MAX_TIMEOUT as String, + "auction.default-timeout-ms": DEFAULT_TIMEOUT as String]) + + def "PBS should apply timeout from stored request when it's not specified in the request"() { + given: "Default AMP request without timeout" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + timeout = null + } + + and: "Default stored request with timeout" + def timeout = PBSUtils.getRandomNumber(0, MAX_TIMEOUT) + def ampStoredRequest = BidRequest.defaultStoredRequest.tap { + tmax = timeout + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getDbStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + prebidServerService.sendAmpRequest(ampRequest) + + then: "Bidder request should contain timeout from the stored request" + def bidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + assert bidderRequest.tmax == timeout as Long + } + + @Unroll + def "PBS should prefer timeout from the request when stored request timeout is #tmax"() { + given: "Default AMP request with timeout" + def timeout = PBSUtils.getRandomNumber(0, MAX_TIMEOUT) + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.timeout = timeout + } + + and: "Default stored request" + def ampStoredRequest = BidRequest.defaultStoredRequest.tap { + it.tmax = tmax + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getDbStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + prebidServerService.sendAmpRequest(ampRequest) + + then: "Bidder request should contain timeout from the request" + def bidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + assert bidderRequest.tmax == timeout as Long + + where: + tmax << [null, PBSUtils.getRandomNumber(0, MAX_TIMEOUT)] + } + + @Unroll + def "PBS should honor max timeout from the settings"() { + given: "Default AMP request" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.timeout = ampRequestTimeout + } + + and: "Default stored request" + def ampStoredRequest = BidRequest.defaultStoredRequest.tap { + it.tmax = storedRequestTimeout + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getDbStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + prebidServerService.sendAmpRequest(ampRequest) + + then: "Bidder request timeout should correspond to the maximum from the settings" + def bidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + assert bidderRequest.tmax == MAX_TIMEOUT as Long + + where: + ampRequestTimeout || storedRequestTimeout + MAX_TIMEOUT + 1 || null + null || MAX_TIMEOUT + 1 + MAX_TIMEOUT + 1 || MAX_TIMEOUT + 1 + } + + def "PBS should honor default timeout"() { + given: "Default AMP request without timeout" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.timeout = null + } + + and: "Default stored request without timeout" + def ampStoredRequest = BidRequest.defaultStoredRequest.tap { + it.tmax = null + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getDbStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + prebidServerService.sendAmpRequest(ampRequest) + + then: "Bidder request timeout should correspond to the maximum from the settings" + def bidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + assert bidderRequest.tmax == DEFAULT_TIMEOUT as Long + } +} diff --git a/src/test/groovy/org/prebid/server/functional/AnalyticsSpec.groovy b/src/test/groovy/org/prebid/server/functional/AnalyticsSpec.groovy new file mode 100644 index 00000000000..a11c759db9c --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/AnalyticsSpec.groovy @@ -0,0 +1,38 @@ +package org.prebid.server.functional + +import org.prebid.server.functional.model.mock.services.pubstack.PubStackResponse +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.testcontainers.Dependencies +import org.prebid.server.functional.testcontainers.PBSTest +import org.prebid.server.functional.testcontainers.scaffolding.PubStackAnalytics +import spock.lang.Retry +import spock.lang.Shared + +@Retry +@PBSTest +class AnalyticsSpec extends BaseSpec { + + private static final String scopeId = UUID.randomUUID() + + @Shared + PubStackAnalytics analytics = new PubStackAnalytics(Dependencies.networkServiceContainer, mapper).tap { + it.setResponse(PubStackResponse.getDefaultPubStackResponse(scopeId, Dependencies.networkServiceContainer.rootUri)) + } + + def "PBS should send PubStack analytics when analytics.pubstack.enabled=true"() { + given: "Pbs config" + def pbsService = pbsServiceFactory.getService(pbsServiceFactory.pubstackAnalyticsConfig(scopeId)) + + and: "Basic bid request" + def bidRequest = BidRequest.defaultBidRequest + + and: "Initial request count" + def analyticsRequestCount = analytics.requestCount + + when: "PBS processes auction request" + pbsService.sendAuctionRequest(bidRequest) + + then: "Analytics request body should be not empty" + assert analytics.requestCount == analyticsRequestCount + 1 + } +} diff --git a/src/test/groovy/org/prebid/server/functional/BaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/BaseSpec.groovy new file mode 100644 index 00000000000..1dfa054409f --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/BaseSpec.groovy @@ -0,0 +1,48 @@ +package org.prebid.server.functional + +import org.prebid.server.functional.repository.HibernateRepositoryService +import org.prebid.server.functional.repository.dao.AccountDao +import org.prebid.server.functional.repository.dao.ConfigDao +import org.prebid.server.functional.repository.dao.StoredImpDao +import org.prebid.server.functional.repository.dao.StoredRequestDao +import org.prebid.server.functional.repository.dao.StoredResponseDao +import org.prebid.server.functional.service.PrebidServerService +import org.prebid.server.functional.testcontainers.PBSTest +import org.prebid.server.functional.testcontainers.PbsServiceFactory +import org.prebid.server.functional.testcontainers.scaffolding.Bidder +import org.prebid.server.functional.testcontainers.scaffolding.PrebidCache +import org.prebid.server.functional.util.ObjectMapperWrapper +import spock.lang.Specification + +import static org.prebid.server.functional.testcontainers.Dependencies.mysqlContainer +import static org.prebid.server.functional.testcontainers.Dependencies.networkServiceContainer +import static org.prebid.server.functional.testcontainers.Dependencies.objectMapperWrapper + +@PBSTest +abstract class BaseSpec extends Specification { + + protected static final ObjectMapperWrapper mapper = objectMapperWrapper + protected static final PbsServiceFactory pbsServiceFactory = new PbsServiceFactory(networkServiceContainer, objectMapperWrapper) + protected static final Bidder bidder = new Bidder(networkServiceContainer, objectMapperWrapper) + protected static final PrebidCache prebidCache = new PrebidCache(networkServiceContainer, objectMapperWrapper) + protected static final PrebidServerService defaultPbsService = pbsServiceFactory.getService([:]) + + protected static final HibernateRepositoryService repository = new HibernateRepositoryService(mysqlContainer) + protected static final AccountDao accountDao = repository.accountDao + protected static final ConfigDao configDao = repository.configDao + protected static final StoredImpDao storedImp = repository.storedImpDao + protected static final StoredRequestDao storedRequestDao = repository.storedRequestDao + protected static final StoredResponseDao storedResponseDao = repository.storedResponseDao + + def setupSpec() { + prebidCache.setResponse() + bidder.setResponse() + } + + def cleanupSpec() { + bidder.reset() + prebidCache.reset() + repository.removeAllDatabaseData() + pbsServiceFactory.stopContainers() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/BidValidationSpec.groovy b/src/test/groovy/org/prebid/server/functional/BidValidationSpec.groovy new file mode 100644 index 00000000000..853cbf9e175 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/BidValidationSpec.groovy @@ -0,0 +1,196 @@ +package org.prebid.server.functional + +import org.prebid.server.functional.model.bidder.BidderName +import org.prebid.server.functional.model.request.auction.App +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.MultiBid +import org.prebid.server.functional.model.request.auction.Site +import org.prebid.server.functional.model.response.auction.Bid +import org.prebid.server.functional.model.response.auction.BidResponse +import org.prebid.server.functional.model.response.auction.ErrorType +import org.prebid.server.functional.service.PrebidServerException +import org.prebid.server.functional.testcontainers.PBSTest +import org.prebid.server.functional.util.PBSUtils +import spock.lang.PendingFeature +import spock.lang.Unroll + +@PBSTest +class BidValidationSpec extends BaseSpec { + + @PendingFeature + def "PBS should return error type invalid bid when bid does not pass validation with error"() { + given: "Default basic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + + and: "Default basic bid with seatbid[].bid[].price = null" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidResponse.seatbid.first().bid.first().price = null + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Response should contain basic fields" + assert response.ext?.errors[ErrorType.GENERIC]*.code == [5] + assert response.ext?.errors[ErrorType.GENERIC]*.message == + ["Bid \"${bidResponse.seatbid.first().bid.first().id}\" does not contain a 'price'" as String] + } + + def "PBS should remove site object and emit warning when both site and app present, debug mode is enabled"() { + given: "Default basic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.site = new Site(id: null, name: PBSUtils.randomString, page: null) + bidRequest.ext.prebid.debug = 1 + + and: "Set app" + bidRequest.app = App.defaultApp + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should not contain site" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert !bidderRequest.site + + and: "Response should contain debug warning" + assert response.ext?.warnings[ErrorType.PREBID]*.message == + ["BidRequest contains app and site. Removed site object"] + } + + def "PBS should remove site object and emit warning when both site and app present, debug mode is disabled"() { + given: "Default basic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.site = new Site(id: null, name: PBSUtils.randomString, page: null) + bidRequest.ext.prebid.debug = 0 + + and: "Set app" + bidRequest.app = App.defaultApp + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should not contain site" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert !bidderRequest.site + + and: "Response should contain debug warning" + assert response.ext?.warnings[ErrorType.PREBID]*.message == + ["BidRequest contains app and site. Removed site object"] + } + + def "PBS should validate site when it is present"() { + given: "Default basic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.site = new Site(id: null, name: PBSUtils.randomString, page: null) + bidRequest.ext.prebid.debug = 1 + + when: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Request should fail with error" + def exception = thrown(PrebidServerException) + assert exception.responseBody.contains("request.site should include at least one of request.site.id or request.site.page") + } + + def "PBS should treat bids with 0 price as valid when deal id is present"() { + given: "Default basic BidRequest with generic bidder" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.debug = 1 + + and: "Bid response with 0 price bid" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidResponse.seatbid.first().bid.first().dealid = PBSUtils.randomNumber + bidResponse.seatbid.first().bid.first().price = 0 + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Zero price bid should be present in the PBS response" + assert response.seatbid?.first()?.bid*.id == [bidResponse.seatbid.first().bid.first().id] + + and: "No errors should be emitted in the debug" + assert !response.ext?.errors + assert !response.ext?.warnings + } + + @Unroll + def "PBS should drop invalid bid and emit debug error when bid price is #bidPrice and deal id is #dealId"() { + given: "Default basic BidRequest with generic bidder" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.debug = 1 + + and: "Bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + def bid = bidResponse.seatbid.first().bid.first() + bid.dealid = dealId + bid.price = bidPrice + def bidId = bid.id + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Invalid bid should be deleted" + assert response.seatbid.size() == 0 + + and: "PBS should emit an error" + assert response.ext?.warnings[ErrorType.PREBID]*.code == [999] + assert response.ext?.warnings[ErrorType.PREBID]*.message == + ["Dropped bid '$bidId'. Does not contain a positive (or zero if there is a deal) 'price'" as String] + + where: + bidPrice | dealId + PBSUtils.randomNegativeNumber | null + PBSUtils.randomNegativeNumber | PBSUtils.randomNumber + 0 | null + null | PBSUtils.randomNumber + null | null + } + + @Unroll + def "PBS should only drop invalid bid without discarding whole seat"() { + given: "Default basic BidRequest with generic bidder" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.debug = 1 + bidRequest.ext.prebid.multibid = [new MultiBid(bidder: BidderName.GENERIC.value, maxBids: 2)] + + and: "Bid response with 2 bids" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidResponse.seatbid[0].bid << Bid.getDefaultBid(bidRequest.imp.first()) + + and: "One of the bids is invalid" + def invalidBid = bidResponse.seatbid.first().bid.first() + invalidBid.dealid = dealId + invalidBid.price = bidPrice + def validBidId = bidResponse.seatbid.first().bid.last().id + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Invalid bids should be deleted" + assert response.seatbid?.first()?.bid*.id == [validBidId] + + and: "PBS should emit an error" + assert response.ext?.warnings[ErrorType.PREBID]*.code == [999] + assert response.ext?.warnings[ErrorType.PREBID]*.message == + ["Dropped bid '$invalidBid.id'. Does not contain a positive (or zero if there is a deal) 'price'" as String] + + where: + bidPrice | dealId + PBSUtils.randomNegativeNumber | null + PBSUtils.randomNegativeNumber | PBSUtils.randomNumber + 0 | null + null | PBSUtils.randomNumber + null | null + } +} diff --git a/src/test/groovy/org/prebid/server/functional/BidderParamsSpec.groovy b/src/test/groovy/org/prebid/server/functional/BidderParamsSpec.groovy new file mode 100644 index 00000000000..228baf16ef2 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/BidderParamsSpec.groovy @@ -0,0 +1,276 @@ +package org.prebid.server.functional + +import io.qameta.allure.Issue +import org.prebid.server.functional.model.bidder.BidderName +import org.prebid.server.functional.model.bidder.Generic +import org.prebid.server.functional.model.db.Account +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.Device +import org.prebid.server.functional.model.request.auction.Geo +import org.prebid.server.functional.model.request.auction.RegsExt +import org.prebid.server.functional.model.request.vtrack.VtrackRequest +import org.prebid.server.functional.model.request.vtrack.xml.Vast +import org.prebid.server.functional.model.response.auction.ErrorType +import org.prebid.server.functional.testcontainers.PBSTest +import org.prebid.server.functional.util.PBSUtils +import spock.lang.Unroll + +import static org.prebid.server.functional.model.bidder.BidderName.APPNEXUS + +@PBSTest +class BidderParamsSpec extends BaseSpec { + + @Unroll + def "PBS should send request to bidder when adapter-defaults.enabled = #adapterDefault and adapters.BIDDER.enabled = #generic"() { + given: "PBS with adapter configuration" + def pbsService = pbsServiceFactory.getService(adapterConfig) + + and: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "Response should contain httpcalls" + assert response?.ext?.debug?.httpcalls[BidderName.GENERIC.value] + + and: "Response should not contain error" + assert !response?.ext?.errors + + where: + adapterDefault | generic | adapterConfig + "true" | "true" | ["adapter-defaults.enabled" : adapterDefault, + "adapters.facebook.enabled" : "false", + "adapters.brightroll.enabled": "false", + "adapters.generic.enabled" : generic] + "false" | "true" | ["adapter-defaults.enabled": adapterDefault, + "adapters.generic.enabled": generic] + } + + @Unroll + def "PBS should not send request to bidder and emit error when adapter-defaults.enabled = #adapterDefault and adapters.BIDDER.enabled = #generic"() { + given: "PBS with adapter configuration" + def pbsService = pbsServiceFactory.getService(adapterConfig) + + and: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "Response should contain error" + assert response?.ext?.errors[ErrorType.GENERIC]*.code == [2] + + where: + adapterDefault | generic | adapterConfig + "false" | "false" | ["adapter-defaults.enabled": adapterDefault, + "adapters.generic.enabled": generic] + "true" | "false" | ["adapter-defaults.enabled" : adapterDefault, + "adapters.facebook.enabled" : "false", + "adapters.brightroll.enabled": "false", + "adapters.generic.enabled" : generic] + } + + @Unroll + def "PBS should modify vast xml when adapter-defaults.modifying-vast-xml-allowed = #adapterDefault and BIDDER.modifying-vast-xml-allowed = #generic"() { + given: "PBS with adapter configuration" + def pbsService = pbsServiceFactory.getService(["adapter-defaults.modifying-vast-xml-allowed": adapterDefault, + "adapters.generic.modifying-vast-xml-allowed": generic]) + + and: "Default VtrackRequest" + String payload = PBSUtils.randomString + def request = VtrackRequest.getDefaultVtrackRequest(mapper.encodeXml(Vast.getDefaultVastModel(payload))) + def accountId = PBSUtils.randomNumber + + and: "Account in the DB" + def account = new Account(uuid: accountId, eventsEnabled: true) + accountDao.save(account) + + when: "PBS processes vtrack request" + pbsService.sendVtrackRequest(request, accountId.toString()) + + then: "vast xml is modified" + def prebidCacheRequest = prebidCache.getXmlRecordedRequestsBody(payload) + assert prebidCacheRequest.size() == 1 + assert prebidCacheRequest.first().contains("/event?t=imp&b=${request.puts[0].bidid}&a=$accountId&bidder=${request.puts[0].bidder}") + + where: + adapterDefault | generic + "true" | "true" + "false" | "true" + } + + @Unroll + def "PBS should not modify vast xml when adapter-defaults.modifying-vast-xml-allowed = #adapterDefault and BIDDER.modifying-vast-xml-allowed = #generic"() { + given: "PBS with adapter configuration" + def pbsService = pbsServiceFactory.getService(["adapter-defaults.modifying-vast-xml-allowed": adapterDefault, + "adapters.generic.modifying-vast-xml-allowed": generic]) + + and: "Default VtrackRequest" + String payload = PBSUtils.randomString + def request = VtrackRequest.getDefaultVtrackRequest(mapper.encodeXml(Vast.getDefaultVastModel(payload))) + def accountId = PBSUtils.randomNumber + + and: "Account in the DB" + def account = new Account(uuid: accountId, eventsEnabled: true) + accountDao.save(account) + + when: "PBS processes vtrack request" + pbsService.sendVtrackRequest(request, accountId.toString()) + + then: "vast xml is not modified" + def prebidCacheRequest = prebidCache.getXmlRecordedRequestsBody(payload) + assert prebidCacheRequest.size() == 1 + assert !prebidCacheRequest.first().contains("/event?t=imp&b=${request.puts[0].bidid}&a=$accountId&bidder=${request.puts[0].bidder}") + + where: + adapterDefault | generic + "true" | "false" + "false" | "false" + } + + @Unroll + def "PBS should mask values when adapter-defaults.pbs-enforces-ccpa = #adapterDefault settings when BIDDER.pbs-enforces-ccpa = #generic"() { + given: "PBS with adapter configuration" + def pbsService = pbsServiceFactory.getService(["adapter-defaults.pbs-enforces-ccpa": adapterDefault, + "adapters.generic.pbs-enforces-ccpa": generic]) + + and: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + def valid_ccpa = "1YYY" + bidRequest.regs.ext = new RegsExt(gdpr: 0, usPrivacy: valid_ccpa) + def lat = PBSUtils.getFractionalRandomNumber(0, 90) + def lon = PBSUtils.getFractionalRandomNumber(0, 90) + bidRequest.device = new Device(geo: new Geo(lat: lat, lon: lon)) + + when: "PBS processes auction request" + pbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should contain masked values" + def bidderRequests = bidder.getBidderRequest(bidRequest.id) + assert bidderRequests.device?.geo?.lat == PBSUtils.getRoundFractionalNumber(lat, 2) + assert bidderRequests.device?.geo?.lon == PBSUtils.getRoundFractionalNumber(lon, 2) + + where: + adapterDefault | generic + "true" | "true" + "false" | "true" + } + + @Unroll + def "PBS should not mask values when adapter-defaults.pbs-enforces-ccpa = #adapterDefault settings when BIDDER.pbs-enforces-ccpa = #generic"() { + given: "PBS with adapter configuration" + def pbsService = pbsServiceFactory.getService(["adapter-defaults.pbs-enforces-ccpa": adapterDefault, + "adapters.generic.pbs-enforces-ccpa": generic]) + + and: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + def valid_ccpa = "1YYY" + bidRequest.regs.ext = new RegsExt(gdpr: 0, usPrivacy: valid_ccpa) + def lat = PBSUtils.getFractionalRandomNumber(0, 90) + def lon = PBSUtils.getFractionalRandomNumber(0, 90) + bidRequest.device = new Device(geo: new Geo(lat: lat, lon: lon)) + + when: "PBS processes auction request" + pbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should contain not masked values" + def bidderRequests = bidder.getBidderRequest(bidRequest.id) + assert bidderRequests.device?.geo?.lat == lat + assert bidderRequests.device?.geo?.lon == lon + + where: + adapterDefault | generic + "true" | "false" + "false" | "false" + } + + def "PBS should prefer bidder params from imp[*].ext.prebid.bidder.BIDDER when ext.prebid.bidderparams.BIDDER is specified"() { + given: "Default basic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + def firstParam = PBSUtils.randomNumber + bidRequest.imp.first().ext.prebid.bidder.generic = new Generic(firstParam: firstParam) + + and: "Set bidderParam to bidRequest" + bidRequest.ext.prebid.bidderParams = [(BidderName.GENERIC): [firstParam: PBSUtils.randomNumber]] + + when: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Response should contain zoneId value from imp[*].ext.prebid.bidder.BIDDER" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.imp[0]?.ext?.bidder?.firstParam == firstParam + } + + def "PBS should send bidder params from imp[*].ext.prebid.bidder.BIDDER when ext.prebid.bidderparams.BIDDER isn't specified"() { + given: "Default basic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + def firstParam = PBSUtils.randomNumber + bidRequest.imp.first().ext.prebid.bidder.generic = new Generic(firstParam: firstParam) + + and: "Set bidderParam = null to bidRequest" + bidRequest.ext.prebid.bidderParams = null + + when: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Response should contain zoneId value from imp[*].ext.prebid.bidder.BIDDER" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.imp[0]?.ext?.bidder?.firstParam == firstParam + } + + def "PBS should merge bidder params from imp[*].ext.prebid.bidder.BIDDER and ext.prebid.bidderparams.BIDDER"() { + given: "Default basic BidRequest with zoneId = null" + def bidRequest = BidRequest.defaultBidRequest + def firstParam = PBSUtils.randomNumber + bidRequest.imp.first().ext.prebid.bidder.generic = new Generic(firstParam: firstParam) + + and: "Set bidderParam to bidRequest" + def secondParam = PBSUtils.randomNumber + bidRequest.ext.prebid.bidderParams = [(BidderName.GENERIC): [secondParam: secondParam]] + + when: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Response should merge bidder params" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.imp[0]?.ext?.bidder?.firstParam == firstParam + assert bidderRequest.imp[0]?.ext?.bidder?.secondParam == secondParam + } + + def "PBS should only send bidder params from ext.prebid.bidderparams.BIDDER to specified bidder"() { + given: "Default basic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + def firstParam = PBSUtils.randomNumber + bidRequest.imp.first().ext.prebid.bidder.generic = new Generic(firstParam: firstParam) + bidRequest.imp.first().ext.prebid.bidder.appNexus = null + + and: "Set bidderParam to bidRequest" + bidRequest.ext.prebid.bidderParams = [(APPNEXUS): [placement_id: PBSUtils.randomNumber]] + + when: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Response shouldn't contain bidder param from another biddder" + bidder.getBidderRequest(bidRequest.id) + } + + // TODO: create same test for enabled circuit breaker + @Issue("https://github.com/prebid/prebid-server-java/issues/1478") + def "PBS should emit warning when bidder endpoint is invalid"() { + given: "Pbs config" + def pbsService = pbsServiceFactory.getService(["adapters.generic.enabled" : "true", + "adapters.generic.endpoint" : "https://", + "http-client.circuit-breaker.enabled": "false"]) + + and: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "Response should contain error" + assert response.ext?.errors[ErrorType.GENERIC]*.code == [999] + assert response.ext?.errors[ErrorType.GENERIC]*.message == ["no empty host accepted"] + } +} diff --git a/src/test/groovy/org/prebid/server/functional/CcpaSpec.groovy b/src/test/groovy/org/prebid/server/functional/CcpaSpec.groovy new file mode 100644 index 00000000000..35d7eba8de6 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/CcpaSpec.groovy @@ -0,0 +1,68 @@ +package org.prebid.server.functional + +import org.prebid.server.functional.model.config.AccountCcpaConfig +import org.prebid.server.functional.model.config.AccountConfig +import org.prebid.server.functional.model.config.AccountPrivacyConfig +import org.prebid.server.functional.model.db.Account +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.Device +import org.prebid.server.functional.model.request.auction.Geo +import org.prebid.server.functional.model.request.auction.RegsExt +import org.prebid.server.functional.testcontainers.PBSTest +import org.prebid.server.functional.util.PBSUtils + +@PBSTest +class CcpaSpec extends BaseSpec { + + // TODO: extend ccpa test with actual fields that we should mask + def "PBS should mask publisher info when privacy.ccpa.enabled = true in account config"() { + given: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + def valid_ccpa = "1YYY" + bidRequest.regs.ext = new RegsExt(gdpr: 0, usPrivacy: valid_ccpa) + def lat = PBSUtils.getFractionalRandomNumber(0, 90) + def lon = PBSUtils.getFractionalRandomNumber(0, 90) + bidRequest.device = new Device(geo: new Geo(lat: lat, lon: lon)) + + and: "Save account config into DB" + def ccpa = new AccountCcpaConfig(enabled: true) + def privacy = new AccountPrivacyConfig(ccpa: ccpa) + def accountConfig = new AccountConfig(privacy: privacy) + def account = new Account(uuid: bidRequest.site.publisher.id, config: accountConfig) + accountDao.save(account) + + when: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should contain masked values" + def bidderRequests = bidder.getBidderRequest(bidRequest.id) + assert bidderRequests.device?.geo?.lat == PBSUtils.getRoundFractionalNumber(lat, 2) + assert bidderRequests.device?.geo?.lon == PBSUtils.getRoundFractionalNumber(lon, 2) + } + + // TODO: extend this ccpa test with actual fields that we should mask + def "PBS should not mask publisher info when privacy.ccpa.enabled = false in account config"() { + given: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + def valid_ccpa = "1YYY" + bidRequest.regs.ext = new RegsExt(gdpr: 0, usPrivacy: valid_ccpa) + def lat = PBSUtils.getFractionalRandomNumber(0, 90) + def lon = PBSUtils.getFractionalRandomNumber(0, 90) + bidRequest.device = new Device(geo: new Geo(lat: lat, lon: lon)) + + and: "Save account config into DB" + def ccpa = new AccountCcpaConfig(enabled: false) + def privacy = new AccountPrivacyConfig(ccpa: ccpa) + def accountConfig = new AccountConfig(privacy: privacy) + def account = new Account(uuid: bidRequest.site.publisher.id, config: accountConfig) + accountDao.save(account) + + when: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should contain not masked values" + def bidderRequests = bidder.getBidderRequest(bidRequest.id) + assert bidderRequests.device?.geo?.lat == lat + assert bidderRequests.device?.geo?.lon == lon + } +} diff --git a/src/test/groovy/org/prebid/server/functional/DealsSpec.groovy b/src/test/groovy/org/prebid/server/functional/DealsSpec.groovy new file mode 100644 index 00000000000..eb970d44328 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/DealsSpec.groovy @@ -0,0 +1,162 @@ +package org.prebid.server.functional + +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.MultiBid +import org.prebid.server.functional.model.request.auction.Targeting +import org.prebid.server.functional.model.response.auction.Bid +import org.prebid.server.functional.model.response.auction.BidResponse +import org.prebid.server.functional.testcontainers.PBSTest +import org.prebid.server.functional.util.PBSUtils +import spock.lang.Unroll + +@PBSTest +class DealsSpec extends BaseSpec { + + @Unroll + def "PBS should choose bid with deal when preferdeals flag equal true"() { + given: "Default basic BidRequest with generic bidder with preferdeals = true" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.targeting = new Targeting(preferdeals: true) + + and: "Bid response with 2 bids" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidResponse.seatbid.first().bid.add(Bid.getDefaultBid(bidRequest.imp.first())) + + and: "One of the bids has dealid" + bidResponse.seatbid.first().bid.first().dealid = PBSUtils.randomNumber + bidResponse.seatbid.first().bid.first().price = dealBidPrice + bidResponse.seatbid.first().bid.last().price = bidPrice + def dealBidId = bidResponse.seatbid.first().bid.first().id + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "PBS should choose bid with deal" + assert response.seatbid?.first()?.bid?.size() == 1 + assert response.seatbid?.first()?.bid?.first()?.id == dealBidId + assert response.seatbid?.first()?.bid?.first()?.price == dealBidPrice + + where: + bidPrice | dealBidPrice + 2 | bidPrice + 1 + 2 | bidPrice - 1 + 0 | 0 + } + + def "PBS should choose higher bid from two bids with deals"() { + given: "Default basic BidRequest with generic bidder with preferdeals = true" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.targeting = new Targeting(preferdeals: true) + + and: "Bid response with 2 bids" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidResponse.seatbid.first().bid.add(Bid.getDefaultBid(bidRequest.imp.first())) + + and: "Both of the bids have dealid" + bidResponse.seatbid.first().bid.each { it.dealid = PBSUtils.randomNumber } + + and: "Set price for bids" + def winningBidPrice = bidResponse.seatbid.first().bid.first().price + 1 + bidResponse.seatbid.first().bid.last().price = winningBidPrice + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "PBS should choose bid with higher deal price" + assert response.seatbid?.first()?.bid?.size() == 1 + assert response.seatbid?.first()?.bid?.first()?.price == winningBidPrice + } + + def "PBS should choose higher bid from two without deals"() { + given: "Default basic BidRequest with generic bidder with preferdeals = true" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.targeting = new Targeting(preferdeals: true) + + and: "Bid response with 2 bids" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidResponse.seatbid.first().bid.add(Bid.getDefaultBid(bidRequest.imp.first())) + + and: "Set price for bids" + def winningBidPrice = bidResponse.seatbid.first().bid.first().price + 1 + bidResponse.seatbid.first().bid.last().price = winningBidPrice + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "PBS should choose bid with higher deal price" + assert response.seatbid?.first()?.bid?.size() == 1 + assert response.seatbid?.first()?.bid?.first()?.price == winningBidPrice + } + + def "PBS should prefer bids with dealid when multibid is enabled"() { + given: "Default basic BidRequest with generic bidder with preferdeals = true" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.targeting = new Targeting(preferdeals: true) + + and: "Set maxbids = 2 for default bidder" + def maxBids = 2 + def multiBid = new MultiBid(bidder: "generic", maxBids: maxBids) + bidRequest.ext.prebid.multibid = [multiBid] + + and: "Bid response with 2 bids" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidResponse.seatbid.first().bid.add(Bid.getDefaultBid(bidRequest.imp.first())) + + and: "Both of the bids have dealid" + bidResponse.seatbid.first().bid.each { it.dealid = PBSUtils.randomNumber } + + and: "Set price for bids" + def higherBidPrice = bidResponse.seatbid.first().bid.first().price + 1 + bidResponse.seatbid.first().bid.last().price = higherBidPrice + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Response should contain maxBids bids with deal" + def bidPrices = response.seatbid?.first()?.bid?.collect { it.price } + assert bidPrices == bidResponse.seatbid.first().bid.collect { it.price }.sort().reverse() + } + + @Unroll + def "PBS should not choose lower deal price with preferdeals equal #preferdeals flag"() { + given: "Default basic BidRequest with generic bidder with preferdeals" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.targeting = new Targeting(preferdeals: preferdeals) + + and: "Bid response with 2 bids" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidResponse.seatbid.first().bid.add(Bid.getDefaultBid(bidRequest.imp.first())) + + and: "One of the bids has dealid" + bidResponse.seatbid.first().bid.first().dealid = PBSUtils.randomNumber + + and: "Set price for bids" + def winningBidPrice = bidResponse.seatbid.first().bid.first().price + 1 + bidResponse.seatbid.first().bid.last().price = winningBidPrice + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "PBS should choose bid with bid with higher price" + assert response.seatbid?.first()?.bid?.size() == 1 + assert response.seatbid?.first()?.bid?.first()?.price == winningBidPrice + + where: + preferdeals << [false, null] + } +} diff --git a/src/test/groovy/org/prebid/server/functional/DebugSpec.groovy b/src/test/groovy/org/prebid/server/functional/DebugSpec.groovy new file mode 100644 index 00000000000..2454ffbebed --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/DebugSpec.groovy @@ -0,0 +1,312 @@ +package org.prebid.server.functional + +import org.apache.commons.lang3.StringUtils +import org.prebid.server.functional.model.bidder.BidderName +import org.prebid.server.functional.model.config.AccountAuctionConfig +import org.prebid.server.functional.model.config.AccountConfig +import org.prebid.server.functional.model.db.Account +import org.prebid.server.functional.model.db.StoredRequest +import org.prebid.server.functional.model.request.amp.AmpRequest +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.response.auction.ErrorType +import org.prebid.server.functional.testcontainers.PBSTest +import org.prebid.server.functional.util.PBSUtils +import spock.lang.PendingFeature +import spock.lang.Unroll + +@PBSTest +class DebugSpec extends BaseSpec { + + private static final String overrideToken = PBSUtils.randomString + + @Unroll + def "PBS should return debug information when debug flag is #debug and test flag is #test"() { + given: "Default BidRequest with test flag" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.debug = debug + bidRequest.test = test + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Response should contain ext.debug" + assert response.ext?.debug + + where: + debug | test + 1 | null + 1 | 0 + null | 1 + } + + @Unroll + def "PBS shouldn't return debug information when debug flag is #debug and test flag is #test"() { + given: "Default BidRequest with test flag" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.debug = test + bidRequest.test = test + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Response shouldn't contain ext.debug" + assert !response.ext?.debug + + where: + debug | test + 0 | null + null | 0 + } + + def "PBS should not return debug information when bidder-level setting debug.allowed = false"() { + given: "Pbs config" + def pbsService = pbsServiceFactory.getService(["adapters.generic.debug.allow": "false"]) + + and: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.debug = 1 + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "Response should not contain ext.debug" + assert !response.ext?.debug?.httpcalls + + and: "Response should contain specific code and text in ext.warnings.general" + assert response.ext?.warnings[ErrorType.PREBID]?.collect { it.code } == [999] // [10003] + assert response.ext?.warnings[ErrorType.PREBID]?.collect { it.message } == + ["Debug turned off for bidder: $BidderName.GENERIC.value" as String] + } + + def "PBS should return debug information when bidder-level setting debug.allowed = true"() { + given: "Pbs config" + def pbsService = pbsServiceFactory.getService(["adapters.generic.debug.allow": "true"]) + + and: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.debug = 1 + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "Response should contain ext.debug" + assert response.ext?.debug?.httpcalls[BidderName.GENERIC.value] + + and: "Response should not contain ext.warnings" + assert !response.ext?.warnings + } + + def "PBS should not return debug information when bidder-level setting debug.allowed = false is overridden by account-level setting debug-allowed = false"() { + given: "Pbs config" + def pbsService = pbsServiceFactory.getService(["adapters.generic.debug.allow": "false"]) + + and: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.debug = 1 + + and: "Account in the DB" + def accountConfig = new AccountConfig(auction: new AccountAuctionConfig(debugAllow: false)) + def account = new Account(uuid: bidRequest.site.publisher.id, config: accountConfig) + accountDao.save(account) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "Response should not contain ext.debug" + assert !response.ext?.debug + + and: "Response should contain specific code and text in ext.warnings.general" + //TODO change to 10002 after updating debug warnings + assert response.ext?.warnings[ErrorType.PREBID]?.collect { it.code } == [999] + //TODO possibly change message after clarifications + assert response.ext?.warnings[ErrorType.PREBID]?.collect { it.message } == + ["Debug turned off for account"] + } + + def "PBS should not return debug information when bidder-level setting debug.allowed = false is overridden by account-level setting debug-allowed = true"() { + given: "Pbs config" + def pbsService = pbsServiceFactory.getService(["adapters.generic.debug.allow": "false"]) + + and: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.debug = 1 + + and: "Account in the DB" + def accountConfig = new AccountConfig(auction: new AccountAuctionConfig(debugAllow: true)) + def account = new Account(uuid: bidRequest.site.publisher.id, config: accountConfig) + accountDao.save(account) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "Response should not contain ext.debug" + assert !response.ext?.debug?.httpcalls + + and: "Response should contain specific code and text in ext.warnings.general" + //TODO change to 10003 after updating debug warnings + assert response.ext?.warnings[ErrorType.PREBID]?.collect { it.code } == [999] + assert response.ext?.warnings[ErrorType.PREBID]?.collect { it.message } == + ["Debug turned off for bidder: $BidderName.GENERIC.value" as String] + } + + def "PBS should not return debug information when bidder-level setting debug.allowed = true is overridden by account-level setting debug-allowed = false"() { + given: "Pbs config" + def pbsService = pbsServiceFactory.getService(["adapters.generic.debug.allow": "true"]) + + and: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.debug = 1 + + and: "Account in the DB" + def accountConfig = new AccountConfig(auction: new AccountAuctionConfig(debugAllow: false)) + def account = new Account(uuid: bidRequest.site.publisher.id, config: accountConfig) + accountDao.save(account) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "Response should not contain ext.debug" + assert !response.ext?.debug + + and: "Response should contain specific code and text in ext.warnings.general" + //TODO change to 10002 after updating debug warnings + assert response.ext?.warnings[ErrorType.PREBID]?.collect { it.code } == [999] + assert response.ext?.warnings[ErrorType.PREBID]?.collect { it.message } == ["Debug turned off for account"] + } + + def "PBS should use default values = true for bidder-level setting debug.allow and account-level setting debug-allowed when they are not specified"() { + given: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.debug = 1 + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Response should contain ext.debug" + assert response.ext?.debug?.httpcalls[BidderName.GENERIC.value] + + and: "Response should not contain ext.warnings" + assert !response.ext?.warnings + } + + @Unroll + def "PBS should return debug information when bidder-level setting debug.allowed = #debugAllowedConfig and account-level setting debug-allowed = #debugAllowedAccount is overridden by x-pbs-debug-override header"() { + given: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.debug = 1 + + and: "Account in the DB" + def accountConfig = new AccountConfig(auction: new AccountAuctionConfig(debugAllow: debugAllowedAccount)) + def account = new Account(uuid: bidRequest.site.publisher.id, config: accountConfig) + accountDao.save(account) + + when: "PBS processes auction request" + def response = pbdService.sendAuctionRequest(bidRequest, ["x-pbs-debug-override": overrideToken]) + + then: "Response should contain ext.debug" + assert response.ext?.debug?.httpcalls[BidderName.GENERIC.value] + + and: "Response should not contain ext.warnings" + assert !response.ext?.warnings + + where: + debugAllowedConfig | debugAllowedAccount | pbdService + false | true | pbsServiceFactory.getService(["debug.override-token" : overrideToken, + "adapters.generic.debug.allow": "false"]) + true | false | pbsServiceFactory.getService(["debug.override-token" : overrideToken, + "adapters.generic.debug.allow": "true"]) + false | false | pbsServiceFactory.getService(["debug.override-token" : overrideToken, + "adapters.generic.debug.allow": "false"]) + } + + @Unroll + def "PBS should not return debug information when x-pbs-debug-override header is incorrect"() { + given: "Pbs config" + def pbsService = pbsServiceFactory.getService(["debug.override-token": overrideToken]) + + and: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.debug = 1 + + and: "Account in the DB" + def accountConfig = new AccountConfig(auction: new AccountAuctionConfig(debugAllow: false)) + def account = new Account(uuid: bidRequest.site.publisher.id, config: accountConfig) + accountDao.save(account) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest, ["x-pbs-debug-override": headerValue]) + + then: "Response should not contain ext.debug" + assert !response.ext?.debug + + and: "Response should contain specific code and text in ext.warnings.general" + //TODO change to 10002 after updating debug warnings + assert response.ext?.warnings[ErrorType.PREBID]?.collect { it.code } == [999] + assert response.ext?.warnings[ErrorType.PREBID]?.collect { it.message } == ["Debug turned off for account"] + + where: + headerValue << [StringUtils.swapCase(overrideToken), PBSUtils.randomString] + } + + @PendingFeature + @Unroll + def "PBS AMP should return debug information when request flag is #requestDebug and store request flag is #storedRequestDebug"() { + given: "Default AMP request" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + debug = requestDebug + } + + and: "Default stored request" + def ampStoredRequest = BidRequest.defaultStoredRequest.tap { + ext.prebid.debug = storedRequestDebug + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getDbStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + def response = defaultPbsService.sendAmpRequest(ampRequest) + + then: "Response should contain debug information" + assert response.debug + + where: + requestDebug || storedRequestDebug + 1 || 0 + 1 || 1 + 1 || null + null || 1 + } + + @Unroll + def "PBS AMP shouldn't return debug information when request flag is #requestDebug and stored request flag is #storedRequestDebug"() { + given: "Default AMP request" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + debug = requestDebug + } + + and: "Default stored request" + def ampStoredRequest = BidRequest.defaultStoredRequest.tap { + ext.prebid.debug = storedRequestDebug + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getDbStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + def response = defaultPbsService.sendAmpRequest(ampRequest) + + then: "Response shouldn't contain debug information" + assert !response.debug + + where: + requestDebug || storedRequestDebug + 0 || 1 + 0 || 0 + 0 || null + null || 0 + null || null + } +} diff --git a/src/test/groovy/org/prebid/server/functional/HttpInteractionSpec.groovy b/src/test/groovy/org/prebid/server/functional/HttpInteractionSpec.groovy new file mode 100644 index 00000000000..63beb4df3cf --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/HttpInteractionSpec.groovy @@ -0,0 +1,120 @@ +package org.prebid.server.functional + +import org.prebid.server.functional.model.bidder.BidderName +import org.prebid.server.functional.model.bidder.Generic +import org.prebid.server.functional.model.bidder.Rubicon +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.logging.httpinteraction.HttpInteractionRequest +import org.prebid.server.functional.testcontainers.PBSTest +import org.prebid.server.functional.util.PBSUtils + +import java.time.Instant + +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.RUBICON + +@PBSTest +class HttpInteractionSpec extends BaseSpec { + + def "PBS should only log request to the specified adapter"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default httpInteractionRequest with specified bidder" + def request = HttpInteractionRequest.defaultHttpInteractionRequest + request.bidder = GENERIC + + and: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.imp.first().ext.prebid.bidder.generic = new Generic() + bidRequest.imp.first().ext.prebid.bidder.rubicon = new Rubicon(accountId: PBSUtils.randomNumber, + siteId: PBSUtils.randomNumber, zoneId: PBSUtils.randomNumber) + + when: "PBS processes bidders params request" + defaultPbsService.sendLoggingHttpInteractionRequest(request) + + and: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "PBS log should contain request to allowed adapter" + def logs = defaultPbsService.getLogsByTime(startTime) + def genericBidderLogs = getLogsByBidder(logs, GENERIC) + def rubiconBidderLogs = getLogsByBidder(logs, RUBICON) + assert getLogsByText(genericBidderLogs, bidRequest.id).size() == 1 + assert getLogsByText(rubiconBidderLogs, bidRequest.id).size() == 0 + } + + def "PBS should not log request to adapter when it is not allowed"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default basic generic BidRequest" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.imp.first().ext.prebid.bidder.generic = new Generic() + + when: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "PBS log should not contain request to adapter" + def logs = defaultPbsService.getLogsByTime(startTime) + def genericBidderLogs = getLogsByBidder(logs, GENERIC) + assert getLogsByText(genericBidderLogs, bidRequest.id).size() == 0 + } + + def "PBS log request to specific adapter should contain only bid params for the named bidder"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default httpInteractionRequest with specified bidder" + def request = HttpInteractionRequest.defaultHttpInteractionRequest + request.bidder = GENERIC + + and: "Default basic generic BidRequest with rubicon" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.imp.first().ext.prebid.bidder.generic = new Generic() + bidRequest.imp.first().ext.prebid.bidder.rubicon = new Rubicon(accountId: PBSUtils.randomNumber, + siteId: PBSUtils.randomNumber, zoneId: PBSUtils.randomNumber) + + when: "PBS processes bidders params request" + defaultPbsService.sendLoggingHttpInteractionRequest(request) + + and: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Extract request from logs" + def logs = defaultPbsService.getLogsByTime(startTime) + assert logs.size() > 0 + def genericBidderLogs = getLogsByBidder(logs, GENERIC) + assert genericBidderLogs.size() > 0 + def idLogs = getLogsByText(genericBidderLogs, bidRequest.id) + assert idLogs.size() == 1 + + def requestLog = getRequestFromLog(idLogs.first(), request.bidder.value) + def retrievedRequest = mapper.decode(requestLog, BidRequest) + + and: "Logged request should not contain bidder parameters in imp[].ext.prebid.bidder.BIDDER" + assert !retrievedRequest?.imp?.first()?.ext?.prebid?.bidder?.generic + assert !retrievedRequest?.imp?.first()?.ext?.prebid?.bidder?.rubicon + + and: "Logged request should contain bidder parameters in imp[].ext.BIDDER" + assert retrievedRequest?.imp?.first()?.ext?.generic + + and: "Logged request should contain only bidder parameters for the named bidder" + assert !retrievedRequest?.imp?.first()?.ext?.rubicon + } + + private static List getLogsByBidder(List logs, BidderName bidderName) { + logs.findAll { it.contains("Request body to ${bidderName.value}:") } + } + + private static List getLogsByText(List logs, String text) { + logs.findAll { it.contains(text) } + } + + private static String getRequestFromLog(String log, String bidderName) { + def logText = "Request body to ${bidderName}:" + + log.substring(log.indexOf(logText) + logText.length() + 2, log.length() - 1) + .replace("\n", "") + } +} diff --git a/src/test/groovy/org/prebid/server/functional/HttpSettingsSpec.groovy b/src/test/groovy/org/prebid/server/functional/HttpSettingsSpec.groovy new file mode 100644 index 00000000000..4425998ba87 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/HttpSettingsSpec.groovy @@ -0,0 +1,164 @@ +package org.prebid.server.functional + +import org.prebid.server.functional.model.db.StoredRequest +import org.prebid.server.functional.model.mock.services.httpsettings.HttpAccountsResponse +import org.prebid.server.functional.model.request.amp.AmpRequest +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.event.EventRequest +import org.prebid.server.functional.model.request.setuid.SetuidRequest +import org.prebid.server.functional.model.request.setuid.UidsCookie +import org.prebid.server.functional.model.request.vtrack.VtrackRequest +import org.prebid.server.functional.model.request.vtrack.xml.Vast +import org.prebid.server.functional.service.PrebidServerException +import org.prebid.server.functional.service.PrebidServerService +import org.prebid.server.functional.testcontainers.Dependencies +import org.prebid.server.functional.testcontainers.PBSTest +import org.prebid.server.functional.testcontainers.scaffolding.HttpSettings +import org.prebid.server.functional.util.PBSUtils +import org.prebid.server.util.ResourceUtil +import spock.lang.Shared + +@PBSTest +class HttpSettingsSpec extends BaseSpec { +// Check that PBS actually applied account config only possible by relying on side effects. + + @Shared + HttpSettings httpSettings = new HttpSettings(Dependencies.networkServiceContainer, mapper) + + @Shared + PrebidServerService prebidServerService = pbsServiceFactory.getService(pbsServiceFactory.httpSettings()) + + def "PBS should take account information from http data source on auction request"() { + given: "Get basic BidRequest with generic bidder and set gdpr = 1" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.regs.ext.gdpr = 1 + + and: "Prepare default account response with gdpr = 0" + def httpSettingsResponse = HttpAccountsResponse.getDefaultHttpAccountsResponse(bidRequest?.site?.publisher?.id) + httpSettings.setResponse(bidRequest?.site?.publisher?.id, httpSettingsResponse) + + when: "PBS processes auction request" + def response = prebidServerService.sendAuctionRequest(bidRequest) + + then: "Response should contain basic fields" + assert response.id + assert response.seatbid?.size() == 1 + assert response.seatbid.first().seat == "generic" + assert response.seatbid?.first()?.bid?.size() == 1 + + and: "There should be only one call to bidder" + assert bidder.getRequestCount(bidRequest.id) == 1 + + and: "There should be only one account request" + assert httpSettings.getRequestCount(bidRequest?.site?.publisher?.id) == 1 + } + + def "PBS should take account information from http data source on AMP request"() { + given: "Default AmpRequest" + def ampRequest = AmpRequest.defaultAmpRequest + + and: "Get basic stored request and set gdpr = 1" + def ampStoredRequest = BidRequest.defaultBidRequest + ampStoredRequest.site.publisher.id = ampRequest.account + ampStoredRequest.regs.ext.gdpr = 1 + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getDbStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + and: "Prepare default account response with gdpr = 0" + def httpSettingsResponse = HttpAccountsResponse.getDefaultHttpAccountsResponse(ampRequest.account.toString()) + httpSettings.setResponse(ampRequest.account.toString(), httpSettingsResponse) + + when: "PBS processes amp request" + def response = prebidServerService.sendAmpRequest(ampRequest) + + then: "Response should contain httpcalls" + assert !response.debug?.httpcalls?.isEmpty() + + and: "There should be only one account request" + assert httpSettings.getRequestCount(ampRequest.account.toString()) == 1 + + then: "Response should contain targeting" + assert !response.debug?.httpcalls?.isEmpty() + } + + def "PBS should take account information from http data source on event request"() { + given: "Default EventRequest" + def eventRequest = EventRequest.defaultEventRequest + + and: "Prepare default account response" + def httpSettingsResponse = HttpAccountsResponse.getDefaultHttpAccountsResponse(eventRequest.accountId.toString()) + httpSettings.setResponse(eventRequest.accountId.toString(), httpSettingsResponse) + + when: "PBS processes event request" + def responseBody = prebidServerService.sendEventRequest(eventRequest) + + then: "Event response should contain and corresponding content-type" + assert responseBody == + ResourceUtil.readByteArrayFromClassPath("org/prebid/server/functional/tracking-pixel.png") + + and: "There should be only one account request" + assert httpSettings.getRequestCount(eventRequest.accountId.toString()) == 1 + } + + def "PBS should take account information from http data source on setuid request"() { + given: "Get default SetuidRequest and set account, gdpr=1 " + def request = SetuidRequest.defaultSetuidRequest + request.gdpr = 1 + request.account = PBSUtils.randomNumber.toString() + def uidsCookie = UidsCookie.defaultUidsCookie + + and: "Prepare default account response" + def httpSettingsResponse = HttpAccountsResponse.getDefaultHttpAccountsResponse(request.account) + httpSettings.setResponse(request.account, httpSettingsResponse) + + when: "PBS processes setuid request" + def response = prebidServerService.sendSetUidRequest(request, uidsCookie) + + then: "Response should contain uids cookie" + assert response.uidsCookie + assert !response.responseBody?.isEmpty() + + and: "There should be only one account request" + assert httpSettings.getRequestCount(request.account) == 1 + } + + def "PBS should take account information from http data source on vtrack request"() { + given: "Default VtrackRequest" + String payload = PBSUtils.randomString + def request = VtrackRequest.getDefaultVtrackRequest(mapper.encodeXml(Vast.getDefaultVastModel(payload))) + def accountId = PBSUtils.randomNumber.toString() + + and: "Prepare default account response" + def httpSettingsResponse = HttpAccountsResponse.getDefaultHttpAccountsResponse(accountId) + httpSettings.setResponse(accountId, httpSettingsResponse) + + when: "PBS processes vtrack request" + def response = prebidServerService.sendVtrackRequest(request, accountId) + + then: "Response should contain uid" + assert response.responses[0]?.uuid + + and: "There should be only one account request and pbc request" + assert httpSettings.getRequestCount(accountId.toString()) == 1 + assert prebidCache.getXmlRequestCount(payload) == 1 + + and: "VastXml that was send to PrebidCache must contain event url" + def prebidCacheRequest = prebidCache.getXmlRecordedRequestsBody(payload)[0] + assert prebidCacheRequest.contains("/event?t=imp&b=${request.puts[0].bidid}&a=$accountId&bidder=${request.puts[0].bidder}") + } + + def "PBS should return error if account settings isn't found"() { + given: "Default EventRequest" + def eventRequest = EventRequest.defaultEventRequest + + when: "PBS processes event request" + prebidServerService.sendEventRequest(eventRequest) + + then: "Request should fail with error" + def exception = thrown(PrebidServerException) + assert exception.statusCode == 401 + assert exception.responseBody.contains("Account '$eventRequest.accountId' doesn't support events") + } +} diff --git a/src/test/groovy/org/prebid/server/functional/MetricsSpec.groovy b/src/test/groovy/org/prebid/server/functional/MetricsSpec.groovy new file mode 100644 index 00000000000..339e06c5a91 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/MetricsSpec.groovy @@ -0,0 +1,97 @@ +package org.prebid.server.functional + +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.vtrack.VtrackRequest +import org.prebid.server.functional.model.request.vtrack.xml.Vast +import org.prebid.server.functional.model.response.auction.BidResponse +import org.prebid.server.functional.testcontainers.PBSTest +import org.prebid.server.functional.util.PBSUtils +import spock.lang.PendingFeature + +// TODO: move metrics tests to the respective specifications as the metrics are a part of normal PBS operation +// TODO: this won't work as is for banner type as we need to signal PBS to store bid in the cache +@PBSTest +class MetricsSpec extends BaseSpec { + + def setup() { + // flushing PBS metrics by receiving collected metrics so that each new test works with a fresh state + defaultPbsService.sendCollectedMetricsRequest() + } + + @PendingFeature + def "PBS should update prebid_cache.creative_size.xml metric when xml creative is received"() { + given: "Default VtrackRequest" + def accountId = PBSUtils.randomNumber.toString() + def creative = mapper.encodeXml(Vast.getDefaultVastModel(PBSUtils.randomString)) + def request = VtrackRequest.getDefaultVtrackRequest(creative) + + when: "PBS processes vtrack request" + defaultPbsService.sendVtrackRequest(request, accountId) + + then: "prebid_cache.creative_size.xml metric should be updated" + def metrics = defaultPbsService.sendCollectedMetricsRequest() + def creativeSize = creative.bytes.length + assert metrics["prebid_cache.creative_size.xml"] == creativeSize + assert metrics["prebid_cache.requests.ok"] == 1 + + and: "account..prebid_cache.creative_size.xml should be updated" + assert metrics["account.${accountId}.prebid_cache.creative_size.xml" as String] == creativeSize + assert metrics["account.${accountId}.prebid_cache.requests.ok" as String] == 1 + } + + @PendingFeature + def "PBS should update prebid_cache.creative_size.json metric when json creative is received"() { + given: "Default BidRequest" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.enableCache() + + and: "Default basic bid with banner creative" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + def adm = PBSUtils.randomString + bidResponse.seatbid[0].bid[0].adm = adm + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes amp request" + defaultPbsService.sendAuctionRequest(bidRequest) + + and: "PBS processes collected metrics request" + def metrics = defaultPbsService.sendCollectedMetricsRequest() + + then: "prebid_cache.creative_size.json should be update" + + def creativeSize = adm.bytes.length + assert metrics["prebid_cache.creative_size.json"] == creativeSize + assert metrics["prebid_cache.requests.ok"] == 1 + + and: "account..prebid_cache.creative_size.json should be update" + def accountId = bidRequest.site.publisher.id + assert metrics["account.${accountId}.prebid_cache.requests.ok" as String] == 1 + } + + def "PBS should increase request_time metric when auction was held"() { + given: "Current value of metric request_time" + def initialValue = getCurrentMetricValue("request_time") + + and: "Default basic BidRequest with generic bidder" + def bidRequest = BidRequest.defaultBidRequest + + when: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + and: "PBS processes collected metrics request" + def response = defaultPbsService.sendCollectedMetricsRequest() + + then: "Response should contain metrics" + assert response.size() > 0 + + and: "request_time metric should be increased" + assert response["request_time"] == initialValue + 1 + } + + private static int getCurrentMetricValue(String name) { + def response = defaultPbsService.sendCollectedMetricsRequest() + response[name] as int ?: 0 + } +} diff --git a/src/test/groovy/org/prebid/server/functional/MultibidSpec.groovy b/src/test/groovy/org/prebid/server/functional/MultibidSpec.groovy new file mode 100644 index 00000000000..5bdcfff6b31 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/MultibidSpec.groovy @@ -0,0 +1,63 @@ +package org.prebid.server.functional + +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.MultiBid +import org.prebid.server.functional.model.request.auction.Targeting +import org.prebid.server.functional.model.response.auction.Bid +import org.prebid.server.functional.model.response.auction.BidResponse +import org.prebid.server.functional.testcontainers.PBSTest +import org.prebid.server.functional.util.PBSUtils + +@PBSTest +class MultibidSpec extends BaseSpec { + + def "PBS should not return seatbid[].bid[].ext.prebid.targeting for non-winning bid in multi-bid response when includeBidderKeys = false"() { + given: "Default basic BidRequest with generic bidder with includeBidderKeys = false" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.targeting = new Targeting(includeBidderKeys: false) + + and: "Set maxbids = 2 for default bidder" + def maxBids = 2 + def multiBid = new MultiBid(bidder: "generic", maxBids: maxBids, targetBidderCodePrefix: PBSUtils.randomString) + bidRequest.ext.prebid.multibid = [multiBid] + + and: "Default basic bid" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + def anotherBid = Bid.getDefaultBid(bidRequest.imp.first()).tap { price = bidResponse.seatbid.first().bid.first().price - 0.1 } + bidResponse.seatbid.first().bid.add(anotherBid) + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "PBS should not return targeting for non-winning bid" + assert !response.seatbid?.first()?.bid?.last()?.ext?.prebid?.targeting + } + + def "PBS should return seatbid[].bid[].ext.prebid.targeting for non-winning bid in multi-bid response when includeBidderKeys = true"() { + given: "Default basic BidRequest with generic bidder with includeBidderKeys = true" + def bidRequest = BidRequest.defaultBidRequest + bidRequest.ext.prebid.targeting = new Targeting(includeBidderKeys: true) + + and: "Set maxbids = 2 for default bidder" + def maxBids = 2 + def multiBid = new MultiBid(bidder: "generic", maxBids: maxBids, targetBidderCodePrefix: PBSUtils.randomString) + bidRequest.ext.prebid.multibid = [multiBid] + + and: "Default basic bid" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + def anotherBid = Bid.getDefaultBid(bidRequest.imp.first()).tap { price = bidResponse.seatbid.first().bid.first().price - 0.1 } + bidResponse.seatbid.first().bid.add(anotherBid) + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "PBS should return targeting for non-winning bid" + assert response.seatbid?.first()?.bid?.last()?.ext?.prebid?.targeting + } +} diff --git a/src/test/groovy/org/prebid/server/functional/SchainSpec.groovy b/src/test/groovy/org/prebid/server/functional/SchainSpec.groovy new file mode 100644 index 00000000000..ab44917f56d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/SchainSpec.groovy @@ -0,0 +1,124 @@ +package org.prebid.server.functional + +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.PrebidSchain +import org.prebid.server.functional.model.request.auction.Schain +import org.prebid.server.functional.model.request.auction.SchainNode +import org.prebid.server.functional.model.request.auction.Source +import org.prebid.server.functional.model.request.auction.SourceExt +import org.prebid.server.functional.service.PrebidServerService +import org.prebid.server.functional.testcontainers.PBSTest +import org.prebid.server.functional.util.PBSUtils +import spock.lang.Shared + +@PBSTest +class SchainSpec extends BaseSpec { + + private static final GLOBAL_SCHAIN_NODE = new SchainNode().tap { + asi = "pbshostcompany.com" + sid = "00001" + hp = 1 + rid = "BidRequest" + } + + @Shared + PrebidServerService prebidServerService = pbsServiceFactory.getService(["auction.host-schain-node": mapper.encode(GLOBAL_SCHAIN_NODE)]) + + def "Global schain node should be appended when only ext.prebid.schains exists"() { + given: "Basic bid request" + def bidRequest = BidRequest.defaultBidRequest + + and: "Set default prebid schain to bidRequest" + def schain = defaultSchain + def prebidSchain = new PrebidSchain(bidders: ["generic"], schain: schain) + bidRequest.ext.prebid.schains = [prebidSchain] + + when: "PBS processes auction request" + prebidServerService.sendAuctionRequest(bidRequest) + + then: "Configured schain node should be appended to the end of the node array" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.source?.ext?.schain?.nodes == schain.nodes + GLOBAL_SCHAIN_NODE + } + + def "Global schain node should be appended to the end of the node array when only source.ext.schain exists"() { + given: "Basic bid request" + def bidRequest = BidRequest.defaultBidRequest + + and: "Set default source schain to bidRequest" + def schain = defaultSchain + def sourceExt = new SourceExt(schain: schain) + bidRequest.source = new Source(ext: sourceExt) + + when: "PBS processes auction request" + prebidServerService.sendAuctionRequest(bidRequest) + + then: "Configured schain node should be appended to the end of the node array" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.source?.ext?.schain?.nodes == schain.nodes + GLOBAL_SCHAIN_NODE + } + + def "Global schain node should be appended when both ext.prebid.schains and source.ext.schain exist"() { + given: "Basic bid request" + def bidRequest = BidRequest.defaultBidRequest + + and: "Set default prebid schain to bidRequest" + def schain = defaultSchain + def prebidSchain = new PrebidSchain(bidders: ["generic"], schain: schain) + bidRequest.ext.prebid.schains = [prebidSchain] + + and: "Set default source schain to bidRequest" + def sourceSchain = defaultSchain + def sourceExt = new SourceExt(schain: sourceSchain) + bidRequest.source = new Source(ext: sourceExt) + + when: "PBS processes auction request" + prebidServerService.sendAuctionRequest(bidRequest) + + then: "Configured schain node should be appended to the end of the node array" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.source?.ext?.schain?.nodes == schain.nodes + GLOBAL_SCHAIN_NODE + } + + def "Global schain node should be appended when ext.prebid.schains and source.ext.schain doesn't exist"() { + given: "Basic bid request" + def bidRequest = BidRequest.defaultBidRequest + + when: "PBS processes auction request" + prebidServerService.sendAuctionRequest(bidRequest) + + then: "Configured schain node should be appended to the end of the node array" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.source?.ext?.schain?.nodes == [GLOBAL_SCHAIN_NODE] + } + + def "Global schain node should be appended when ext.prebid.schains applied for unknown bidder"() { + given: "Basic bid request" + def bidRequest = BidRequest.defaultBidRequest + + and: "Set default prebid schain with unknown bidder to bidRequest" + def schain = defaultSchain + def prebidSchain = new PrebidSchain(bidders: ["appnexus"], schain: schain) + bidRequest.ext.prebid.schains = [prebidSchain] + + when: "PBS processes auction request" + prebidServerService.sendAuctionRequest(bidRequest) + + then: "Configured schain node should be appended to the end of the node array" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.source?.ext?.schain?.nodes == [GLOBAL_SCHAIN_NODE] + } + + private static Schain getDefaultSchain() { + def node = new SchainNode().tap { + asi = PBSUtils.randomString + sid = PBSUtils.randomString + hp = PBSUtils.randomNumber + name = PBSUtils.randomString + domain = PBSUtils.randomString + } + new Schain(ver: "1.0", complete: 1, nodes: [node]) + } +} + + diff --git a/src/test/groovy/org/prebid/server/functional/SmokeSpec.groovy b/src/test/groovy/org/prebid/server/functional/SmokeSpec.groovy new file mode 100644 index 00000000000..15bc2e431f4 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/SmokeSpec.groovy @@ -0,0 +1,207 @@ +package org.prebid.server.functional + +import org.prebid.server.functional.model.db.Account +import org.prebid.server.functional.model.db.StoredRequest +import org.prebid.server.functional.model.request.amp.AmpRequest +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.cookiesync.CookieSyncRequest +import org.prebid.server.functional.model.request.event.EventRequest +import org.prebid.server.functional.model.request.logging.httpinteraction.HttpInteractionRequest +import org.prebid.server.functional.model.request.setuid.SetuidRequest +import org.prebid.server.functional.model.request.setuid.UidsCookie +import org.prebid.server.functional.model.request.vtrack.VtrackRequest +import org.prebid.server.functional.model.request.vtrack.xml.Vast +import org.prebid.server.functional.model.response.cookiesync.CookieSyncResponse +import org.prebid.server.functional.testcontainers.PBSTest +import org.prebid.server.functional.util.PBSUtils +import org.prebid.server.util.ResourceUtil + +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.response.status.Status.OK + +@PBSTest +class SmokeSpec extends BaseSpec { + + def "PBS should return BidResponse when there are valid bids"() { + given: "Default basic BidRequest with generic bidder" + def bidRequest = BidRequest.defaultBidRequest + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Response should contain basic fields" + assert response.id == bidRequest.id + assert response.seatbid?.size() == 1 + assert response.seatbid[0]?.seat == "generic" + assert response.seatbid[0]?.bid?.size() == 1 + assert response.seatbid[0]?.bid[0]?.impid == bidRequest.imp[0].id + + and: "Only declared bidders should be called" + def requestBidders = bidRequest.requestBidders + def responseBidders = response.ext?.debug?.bidders + assert responseBidders.keySet() == requestBidders.toSet() + + and: "There should be only one call to bidder" + assert bidder.getRequestCount(bidRequest.id) == 1 + } + + def "PBS should return AMP response"() { + given: "Default AmpRequest" + def ampRequest = AmpRequest.defaultAmpRequest + def ampStoredRequest = BidRequest.defaultBidRequest + ampStoredRequest.site.publisher.id = ampRequest.account + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getDbStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + def response = defaultPbsService.sendAmpRequest(ampRequest) + + then: "Response should contain targeting and httpcalls" + assert response.targeting + assert response.debug.httpcalls + + and: "httpcalls should send request for bidders from storedRequest" + def storedRequestBidders = ampStoredRequest.requestBidders + def responseBidders = response.debug?.bidders + assert responseBidders.keySet() == storedRequestBidders.toSet() + } + + def "Call PBS /cookie_sync without uids cookie should return element.usersync.url"() { + given: "Default CookieSyncRequest" + def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest + + when: "PBS processes cookie sync request" + def response = defaultPbsService.sendCookieSyncRequest(cookieSyncRequest) + + then: "Response should contain all bidders" + assert response.status == CookieSyncResponse.Status.NO_COOKIE + assert response.bidderStatus?.size() == cookieSyncRequest.bidders.size() + def bidderStatus = response.getBidderUsersync(GENERIC) + assert bidderStatus?.usersync?.url + assert bidderStatus?.usersync?.type + } + + def "Call PBS /cookie_sync with valid uids cookie should return status OK"() { + given: "Default CookieSyncRequest" + def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest + def uidsCookie = UidsCookie.defaultUidsCookie + + when: "PBS processes cookie sync request" + def response = defaultPbsService.sendCookieSyncRequest(cookieSyncRequest, uidsCookie) + + then: "Response should contain have status 'OK'" + assert response.status == CookieSyncResponse.Status.OK + + and: "Response should contain all bidders" + assert !response.getBidderUsersync(GENERIC) + } + + def "PBS should set uids cookie"() { + given: "Default SetuidRequest" + def request = SetuidRequest.defaultSetuidRequest + def uidsCookie = UidsCookie.defaultUidsCookie + + when: "PBS processes setuid request" + def response = defaultPbsService.sendSetUidRequest(request, uidsCookie) + + then: "Response should contain uids cookie" + assert response.uidsCookie + assert !response.responseBody?.isEmpty() + } + + def "PBS should get uids cookie"() { + given: "Default uids Cookie" + def uidsCookie = UidsCookie.defaultUidsCookie + + when: "PBS processes getuid request" + def response = defaultPbsService.sendGetUidRequest(uidsCookie) + + then: "Response should contain bidder uids" + assert response.buyeruids?.size() == uidsCookie.tempUIDs.size() + assert response.buyeruids.every { bidder, uid -> uidsCookie.tempUIDs[bidder].uid == uid } + } + + def "PBS should return tracking pixel on event request"() { + given: "Default EventRequest" + def eventRequest = EventRequest.defaultEventRequest + + and: "Account in the DB" + def account = new Account(uuid: eventRequest.accountId, eventsEnabled: true) + accountDao.save(account) + + when: "PBS processes event request" + def responseBody = defaultPbsService.sendEventRequest(eventRequest) + + then: "Event response should contain and corresponding content-type" + assert responseBody == + ResourceUtil.readByteArrayFromClassPath("org/prebid/server/functional/tracking-pixel.png") + } + + def "PBS should return PBC response on vtrack request"() { + given: "Default VtrackRequest" + def payload = PBSUtils.randomNumber.toString() + def request = VtrackRequest.getDefaultVtrackRequest(mapper.encodeXml(Vast.getDefaultVastModel(payload))) + def accountId = PBSUtils.randomNumber.toString() + + when: "PBS processes vtrack request" + def response = defaultPbsService.sendVtrackRequest(request, accountId) + + then: "Response should contain uid" + assert response.responses[0]?.uuid + } + + def "status responds with 200 OK"() { + when: "PBS processes status request" + def response = defaultPbsService.sendStatusRequest() + + then: "Response should contain status OK" + assert response.application?.status == OK + } + + def "PBS should get info about active bidders"() { + when: "PBS processes bidders info request" + def response = defaultPbsService.sendInfoBiddersRequest() + + then: "Response should contain bidders info" + assert !response.isEmpty() + } + + def "PBS should get info about requested bidder"() { + when: "PBS processes bidders info request" + def response = defaultPbsService.sendBidderInfoRequest(GENERIC) + + then: "Response should contain bidder info" + assert response.maintainer?.email == "maintainer@example.com" + assert response.capabilities?.app?.mediaTypes == ["banner", "video", "native", "audio"] + assert response.capabilities?.site?.mediaTypes == ["banner", "video", "native", "audio"] + } + + def "PBS should get bidders params"() { + when: "PBS processes bidders params request" + def response = defaultPbsService.sendBiddersParamsRequest() + + then: "Response should contain bidders params" + assert response.parameters.size() > 0 + } + + def "PBS should return currency rates"() { + when: "PBS processes bidders params request" + def response = defaultPbsService.sendCurrencyRatesRequest() + + then: "Response should contain bidders params" + assert response.rates?.size() > 0 + } + + def "PBS should return empty body on httpinteraction request"() { + given: "Default httpInteractionRequest" + def request = HttpInteractionRequest.defaultHttpInteractionRequest + + when: "PBS processes bidders params request" + def response = defaultPbsService.sendLoggingHttpInteractionRequest(request) + + then: "Response should contain bidders params" + assert response.isEmpty() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/AccountStatus.groovy b/src/test/groovy/org/prebid/server/functional/model/AccountStatus.groovy new file mode 100644 index 00000000000..67c61e52066 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/AccountStatus.groovy @@ -0,0 +1,17 @@ +package org.prebid.server.functional.model + +import com.fasterxml.jackson.annotation.JsonValue + +enum AccountStatus { + + ACTIVE, INACTIVE + + @JsonValue + String getValue() { + name().toLowerCase() + } + + static AccountStatus forValue(String value) { + values().find { it.value == value } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/ResponseModel.groovy b/src/test/groovy/org/prebid/server/functional/model/ResponseModel.groovy new file mode 100644 index 00000000000..91a7f1fe9d0 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/ResponseModel.groovy @@ -0,0 +1,6 @@ +package org.prebid.server.functional.model + +/** + * This marker interface should limit the possible values used by the MockServerClientWrapper. + */ +interface ResponseModel {} diff --git a/src/test/groovy/org/prebid/server/functional/model/bidder/AppNexus.groovy b/src/test/groovy/org/prebid/server/functional/model/bidder/AppNexus.groovy new file mode 100644 index 00000000000..63fb2dd386c --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/bidder/AppNexus.groovy @@ -0,0 +1,23 @@ +package org.prebid.server.functional.model.bidder + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString +import org.prebid.server.functional.util.PBSUtils + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy) +class AppNexus implements BidderAdapter { + + Integer placementId + String invCode + String trafficSourceCode + + static AppNexus getDefault() { + new AppNexus().tap { + placementId = PBSUtils.randomNumber + invCode = PBSUtils.randomString + trafficSourceCode = PBSUtils.randomString + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/bidder/BidderAdapter.groovy b/src/test/groovy/org/prebid/server/functional/model/bidder/BidderAdapter.groovy new file mode 100644 index 00000000000..078415f615e --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/bidder/BidderAdapter.groovy @@ -0,0 +1,8 @@ +package org.prebid.server.functional.model.bidder + +/** + * This marker interface should limit the list of available bidders + */ +interface BidderAdapter { + +} diff --git a/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy b/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy new file mode 100644 index 00000000000..8e92787d6a2 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.bidder + +import com.fasterxml.jackson.annotation.JsonValue + +enum BidderName { + + GENERIC, RUBICON, APPNEXUS + + @JsonValue + String getValue() { + name().toLowerCase() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/bidder/Generic.groovy b/src/test/groovy/org/prebid/server/functional/model/bidder/Generic.groovy new file mode 100644 index 00000000000..e2ddc71dc0c --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/bidder/Generic.groovy @@ -0,0 +1,7 @@ +package org.prebid.server.functional.model.bidder + +class Generic implements BidderAdapter { + + Integer firstParam + Integer secondParam +} diff --git a/src/test/groovy/org/prebid/server/functional/model/bidder/Rubicon.groovy b/src/test/groovy/org/prebid/server/functional/model/bidder/Rubicon.groovy new file mode 100644 index 00000000000..9bc06937e7d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/bidder/Rubicon.groovy @@ -0,0 +1,18 @@ +package org.prebid.server.functional.model.bidder + +import org.prebid.server.functional.util.PBSUtils + +class Rubicon implements BidderAdapter { + + Integer accountId + Integer siteId + Integer zoneId + + static Rubicon getDefault() { + new Rubicon().tap { + accountId = PBSUtils.randomNumber + siteId = PBSUtils.randomNumber + zoneId = PBSUtils.randomNumber + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderImp.groovy b/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderImp.groovy new file mode 100644 index 00000000000..62799412692 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderImp.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.bidderspecific + +import groovy.transform.ToString +import org.prebid.server.functional.model.request.auction.Imp + +@ToString(includeNames = true, ignoreNulls = true) +class BidderImp extends Imp { + + BidderImpExt ext +} diff --git a/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderImpExt.groovy b/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderImpExt.groovy new file mode 100644 index 00000000000..354e5ac7e60 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderImpExt.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.bidderspecific + +import groovy.transform.ToString +import org.prebid.server.functional.model.bidder.Generic +import org.prebid.server.functional.model.request.auction.ImpExt + +@ToString(includeNames = true, ignoreNulls = true) +class BidderImpExt extends ImpExt { + + Generic bidder +} diff --git a/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderRequest.groovy b/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderRequest.groovy new file mode 100644 index 00000000000..6def4eb434b --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/bidderspecific/BidderRequest.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.bidderspecific + +import groovy.transform.ToString +import org.prebid.server.functional.model.request.auction.BidRequest + +@ToString(includeNames = true, ignoreNulls = true) +class BidderRequest extends BidRequest { + + List imp +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/AccountAnalyticsConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/AccountAnalyticsConfig.groovy new file mode 100644 index 00000000000..96dcf8dcf30 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/AccountAnalyticsConfig.groovy @@ -0,0 +1,12 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) +class AccountAnalyticsConfig { + + Map auctionEvents +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy new file mode 100644 index 00000000000..fcad48ccab9 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy @@ -0,0 +1,19 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) +class AccountAuctionConfig { + + String priceGranularity + Integer bannerCacheTtl + Integer videoCacheTtl + Integer truncateTargetAttr + String defaultIntegration + AccountBidValidationConfig bidValidations + AccountEventsConfig events + Boolean debugAllow +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/AccountBidValidationConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/AccountBidValidationConfig.groovy new file mode 100644 index 00000000000..eb6cd4fe882 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/AccountBidValidationConfig.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonProperty +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class AccountBidValidationConfig { + + @JsonProperty("banner-creative-max-size") + BidValidationEnforcement bannerMaxSizeEnforcement +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/AccountCcpaConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/AccountCcpaConfig.groovy new file mode 100644 index 00000000000..dc7d0e27d00 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/AccountCcpaConfig.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) +class AccountCcpaConfig { + + Boolean enabled + Map enabledForRequestType +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/AccountConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/AccountConfig.groovy new file mode 100644 index 00000000000..48dda7885c3 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/AccountConfig.groovy @@ -0,0 +1,21 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString +import org.prebid.server.functional.model.AccountStatus + +@EqualsAndHashCode +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) +class AccountConfig { + + String id + AccountStatus status + AccountAuctionConfig auction + AccountPrivacyConfig privacy + AccountAnalyticsConfig analytics + AccountCookieSyncConfig cookieSync + AccountHooksConfiguration hooks +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/AccountCookieSyncConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/AccountCookieSyncConfig.groovy new file mode 100644 index 00000000000..5c76a655244 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/AccountCookieSyncConfig.groovy @@ -0,0 +1,14 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) +class AccountCookieSyncConfig { + + Integer defaultLimit + Integer maxLimit + Boolean defaultCoopSync +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/AccountEventsConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/AccountEventsConfig.groovy new file mode 100644 index 00000000000..09632c75bef --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/AccountEventsConfig.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.config + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class AccountEventsConfig { + + Boolean enabled +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/AccountGdprConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/AccountGdprConfig.groovy new file mode 100644 index 00000000000..3782cb82e7e --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/AccountGdprConfig.groovy @@ -0,0 +1,17 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) +class AccountGdprConfig { + + Boolean enabled + Map enabledForRequestType + Map purposes + Map specialFeatures + PurposeOneTreatmentInterpretation purposeOneTreatmentInterpretation + List basicEnforcementVendors +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/AccountHooksConfiguration.groovy b/src/test/groovy/org/prebid/server/functional/model/config/AccountHooksConfiguration.groovy new file mode 100644 index 00000000000..2dc0336662e --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/AccountHooksConfiguration.groovy @@ -0,0 +1,12 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) +class AccountHooksConfiguration { + + ExecutionPlan executionPlan +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/AccountPrivacyConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/AccountPrivacyConfig.groovy new file mode 100644 index 00000000000..4cd33fd8a4d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/AccountPrivacyConfig.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) +class AccountPrivacyConfig { + + AccountGdprConfig gdpr + AccountCcpaConfig ccpa +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/BidValidationEnforcement.groovy b/src/test/groovy/org/prebid/server/functional/model/config/BidValidationEnforcement.groovy new file mode 100644 index 00000000000..79dbeace7a4 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/BidValidationEnforcement.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonValue + +enum BidValidationEnforcement { + + SKIP, ENFORCE, WARN + + @JsonValue + String getValue() { + name().toLowerCase() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/ChannelType.groovy b/src/test/groovy/org/prebid/server/functional/model/config/ChannelType.groovy new file mode 100644 index 00000000000..f01abb7800a --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/ChannelType.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonValue + +enum ChannelType { + + WEB, AMP, APP, VIDEO + + @JsonValue + String getValue() { + name().toLowerCase() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/Endpoint.groovy b/src/test/groovy/org/prebid/server/functional/model/config/Endpoint.groovy new file mode 100644 index 00000000000..eee6d80d319 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/Endpoint.groovy @@ -0,0 +1,33 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonValue +import groovy.transform.ToString + +@ToString +enum Endpoint { + + OPENRTB2_AUCTION("/openrtb2/auction"), + OPENRTB2_AMP("/openrtb2/amp"), + OPENRTB2_VIDEO("/openrtb2/video"), + COOKIE_SYNC("/cookie_sync"), + SETUID("/setuid"), + BIDDER_PARAMS("/bidders/params"), + EVENT("/event"), + GETUIDS("/getuids"), + INFO_BIDDERS("/info/bidders"), + OPTOUT("/optout"), + STATUS("/status"), + VTRACK("/vtrack") + + @JsonValue + final String value + + Endpoint(String value) { + this.value = value + } + + @Override + String toString() { + value + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/EndpointExecutionPlan.groovy b/src/test/groovy/org/prebid/server/functional/model/config/EndpointExecutionPlan.groovy new file mode 100644 index 00000000000..81ad1446af0 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/EndpointExecutionPlan.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.config + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class EndpointExecutionPlan { + + Map stages +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/ExecutionGroup.groovy b/src/test/groovy/org/prebid/server/functional/model/config/ExecutionGroup.groovy new file mode 100644 index 00000000000..45f15a29cd8 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/ExecutionGroup.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) +class ExecutionGroup { + + Long timeout + List hookSequence +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/ExecutionPlan.groovy b/src/test/groovy/org/prebid/server/functional/model/config/ExecutionPlan.groovy new file mode 100644 index 00000000000..8fbc9c5ab6c --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/ExecutionPlan.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.config + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class ExecutionPlan { + + Map endpoints +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/HookId.groovy b/src/test/groovy/org/prebid/server/functional/model/config/HookId.groovy new file mode 100644 index 00000000000..765de8d9068 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/HookId.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) +class HookId { + + String moduleCode + String hookImplCode +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/Purpose.groovy b/src/test/groovy/org/prebid/server/functional/model/config/Purpose.groovy new file mode 100644 index 00000000000..b0c92abf621 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/Purpose.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonValue + +enum Purpose { + + P1, P2, P3, P4, P5, P6, P7, P8, P9, P10 + + @JsonValue + String getValue() { + name().toLowerCase() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/PurposeConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/PurposeConfig.groovy new file mode 100644 index 00000000000..e944200980c --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/PurposeConfig.groovy @@ -0,0 +1,14 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) +class PurposeConfig { + + PurposeEnforcement enforcePurpose + Boolean enforceVendors + List vendorExceptions +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/PurposeEnforcement.groovy b/src/test/groovy/org/prebid/server/functional/model/config/PurposeEnforcement.groovy new file mode 100644 index 00000000000..42664e52fe5 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/PurposeEnforcement.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonValue + +enum PurposeEnforcement { + + NO, BASIC, FULL + + @JsonValue + String getValue() { + name().toLowerCase() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/PurposeOneTreatmentInterpretation.groovy b/src/test/groovy/org/prebid/server/functional/model/config/PurposeOneTreatmentInterpretation.groovy new file mode 100644 index 00000000000..427b3b630fe --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/PurposeOneTreatmentInterpretation.groovy @@ -0,0 +1,24 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonValue +import groovy.transform.ToString + +@ToString +enum PurposeOneTreatmentInterpretation { + + IGNORE("ignore"), + NO_ACCESS_ALLOWED("no-access-allowed"), + ACCESS_ALLOWED("access-allowed") + + @JsonValue + final String value + + PurposeOneTreatmentInterpretation(String value) { + this.value = value + } + + @Override + String toString() { + value + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/SpecialFeature.groovy b/src/test/groovy/org/prebid/server/functional/model/config/SpecialFeature.groovy new file mode 100644 index 00000000000..1afb0f2deed --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/SpecialFeature.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonValue + +enum SpecialFeature { + + SF1, SF2 + + @JsonValue + String getValue() { + name().toLowerCase() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/SpecialFeatureConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/SpecialFeatureConfig.groovy new file mode 100644 index 00000000000..e8c9cf96b72 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/SpecialFeatureConfig.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) +class SpecialFeatureConfig { + + Boolean enforce + List vendorExceptions +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/Stage.groovy b/src/test/groovy/org/prebid/server/functional/model/config/Stage.groovy new file mode 100644 index 00000000000..44c6930d869 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/Stage.groovy @@ -0,0 +1,28 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonValue +import groovy.transform.ToString + +@ToString +enum Stage { + + ENTRYPOINT("entrypoint"), + RAW_AUCTION_REQUEST("raw-auction-request"), + PROCESSED_AUCTION_REQUEST("processed-auction-request"), + BIDDER_REQUEST("bidder-request"), + RAW_BIDDER_RESPONSE("raw-bidder-response"), + PROCESSED_BIDDER_RESPONSE("processed-bidder-response"), + AUCTION_RESPONSE("auction-response") + + @JsonValue + final String value + + Stage(String value) { + this.value = value + } + + @Override + String toString() { + value + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/StageExecutionPlan.groovy b/src/test/groovy/org/prebid/server/functional/model/config/StageExecutionPlan.groovy new file mode 100644 index 00000000000..33023c58176 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/StageExecutionPlan.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.config + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class StageExecutionPlan { + + List groups +} diff --git a/src/test/groovy/org/prebid/server/functional/model/db/Account.groovy b/src/test/groovy/org/prebid/server/functional/model/db/Account.groovy new file mode 100644 index 00000000000..bb089280334 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/db/Account.groovy @@ -0,0 +1,60 @@ +package org.prebid.server.functional.model.db + +import groovy.transform.ToString +import javax.persistence.Column +import javax.persistence.Convert +import javax.persistence.Entity +import javax.persistence.GeneratedValue +import javax.persistence.Id +import javax.persistence.Table +import org.prebid.server.functional.model.AccountStatus +import org.prebid.server.functional.model.config.AccountConfig +import org.prebid.server.functional.model.db.typeconverter.AccountConfigTypeConverter +import org.prebid.server.functional.model.db.typeconverter.AccountStatusTypeConverter + +import java.sql.Timestamp + +import static javax.persistence.GenerationType.IDENTITY + +@Entity +@Table(name = "accounts_account") +@ToString(includeNames = true) +class Account { + + @Id + @GeneratedValue(strategy = IDENTITY) + @Column(name = "id") + Integer id + @Column(name = "uuid") + String uuid + @Column(name = "price_granularity") + String priceGranularity + @Column(name = "banner_cache_ttl") + Integer bannerCacheTtl + @Column(name = "video_cache_ttl") + Integer videoCacheTtl + @Column(name = "events_enabled") + Boolean eventsEnabled + @Column(name = "tcf_config") + String tcfConfig + @Column(name = "truncate_target_attr") + Integer truncateTargetAttr + @Column(name = "default_integration") + String defaultIntegration + @Column(name = "analytics_config") + String analyticsConfig + @Column(name = "bid_validations") + String bidValidations + @Column(name = "status") + @Convert(converter = AccountStatusTypeConverter) + AccountStatus status + @Column(name = "config") + @Convert(converter = AccountConfigTypeConverter) + AccountConfig config + @Column(name = "updated_by") + Integer updatedBy + @Column(name = "updated_by_user") + String updatedByUser + @Column(name = "updated") + Timestamp updated +} diff --git a/src/test/groovy/org/prebid/server/functional/model/db/S2sConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/db/S2sConfig.groovy new file mode 100644 index 00000000000..d767c5cf4f6 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/db/S2sConfig.groovy @@ -0,0 +1,25 @@ +package org.prebid.server.functional.model.db + +import groovy.transform.ToString +import javax.persistence.Column +import javax.persistence.Entity +import javax.persistence.GeneratedValue +import javax.persistence.Id +import javax.persistence.Table + +import static javax.persistence.GenerationType.IDENTITY + +@Entity +@Table(name = "s2sconfig_config") +@ToString(includeNames = true) +class S2sConfig { + + @Id + @GeneratedValue(strategy = IDENTITY) + @Column(name = "id") + Integer id + @Column(name = "uuid", nullable = false) + String uuid + @Column(name = "config") + String config +} diff --git a/src/test/groovy/org/prebid/server/functional/model/db/StoredImp.groovy b/src/test/groovy/org/prebid/server/functional/model/db/StoredImp.groovy new file mode 100644 index 00000000000..4152bea362b --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/db/StoredImp.groovy @@ -0,0 +1,29 @@ +package org.prebid.server.functional.model.db + +import groovy.transform.ToString +import javax.persistence.Column +import javax.persistence.Convert +import javax.persistence.Entity +import javax.persistence.GeneratedValue +import javax.persistence.Id +import javax.persistence.Table +import org.prebid.server.functional.model.db.typeconverter.ImpConfigTypeConverter +import org.prebid.server.functional.model.request.auction.Imp + +import static javax.persistence.GenerationType.IDENTITY + +@Entity +@Table(name = "stored_imps") +@ToString(includeNames = true) +class StoredImp { + + @Id + @GeneratedValue(strategy = IDENTITY) + @Column(name = "id") + Integer id + @Column(name = "uuid") + String uuid + @Column(name = "config") + @Convert(converter = ImpConfigTypeConverter) + Imp config +} diff --git a/src/test/groovy/org/prebid/server/functional/model/db/StoredRequest.groovy b/src/test/groovy/org/prebid/server/functional/model/db/StoredRequest.groovy new file mode 100644 index 00000000000..0b41da34e10 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/db/StoredRequest.groovy @@ -0,0 +1,36 @@ +package org.prebid.server.functional.model.db + +import groovy.transform.ToString +import javax.persistence.Column +import javax.persistence.Convert +import javax.persistence.Entity +import javax.persistence.GeneratedValue +import javax.persistence.Id +import javax.persistence.Table +import org.prebid.server.functional.model.db.typeconverter.StoredRequestConfigTypeConverter +import org.prebid.server.functional.model.request.amp.AmpRequest +import org.prebid.server.functional.model.request.auction.BidRequest + +import static javax.persistence.GenerationType.IDENTITY + +@Entity +@Table(name = "stored_requests") +@ToString(includeNames = true) +class StoredRequest { + + @Id + @GeneratedValue(strategy = IDENTITY) + @Column(name = "id") + Integer id + @Column(name = "accountId") + String accountId + @Column(name = "reqid") + String reqid + @Column(name = "requestData") + @Convert(converter = StoredRequestConfigTypeConverter) + BidRequest requestData + + static StoredRequest getDbStoredRequest(AmpRequest ampRequest, BidRequest bidRequest) { + new StoredRequest(reqid: ampRequest.tagId, accountId: ampRequest.account, requestData: bidRequest) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/db/StoredResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/db/StoredResponse.groovy new file mode 100644 index 00000000000..2e75ddd9144 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/db/StoredResponse.groovy @@ -0,0 +1,29 @@ +package org.prebid.server.functional.model.db + +import groovy.transform.ToString +import javax.persistence.Column +import javax.persistence.Convert +import javax.persistence.Entity +import javax.persistence.GeneratedValue +import javax.persistence.Id +import javax.persistence.Table +import org.prebid.server.functional.model.db.typeconverter.StoredResponseConfigTypeConverter +import org.prebid.server.functional.model.response.auction.BidResponse + +import static javax.persistence.GenerationType.IDENTITY + +@Entity +@Table(name = "stored_responses") +@ToString(includeNames = true) +class StoredResponse { + + @Id + @GeneratedValue(strategy = IDENTITY) + @Column(name = "id") + Integer id + @Column(name = "uuid") + String uuid + @Column(name = "config") + @Convert(converter = StoredResponseConfigTypeConverter) + BidResponse config +} diff --git a/src/test/groovy/org/prebid/server/functional/model/db/typeconverter/AccountConfigTypeConverter.groovy b/src/test/groovy/org/prebid/server/functional/model/db/typeconverter/AccountConfigTypeConverter.groovy new file mode 100644 index 00000000000..42a11b145b8 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/db/typeconverter/AccountConfigTypeConverter.groovy @@ -0,0 +1,18 @@ +package org.prebid.server.functional.model.db.typeconverter + +import javax.persistence.AttributeConverter +import org.prebid.server.functional.model.config.AccountConfig +import org.prebid.server.functional.testcontainers.Dependencies + +class AccountConfigTypeConverter implements AttributeConverter { + + @Override + String convertToDatabaseColumn(AccountConfig accountConfig) { + accountConfig ? Dependencies.objectMapperWrapper.encode(accountConfig) : null + } + + @Override + AccountConfig convertToEntityAttribute(String value) { + value ? Dependencies.objectMapperWrapper.decode(value, AccountConfig) : null + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/db/typeconverter/AccountStatusTypeConverter.groovy b/src/test/groovy/org/prebid/server/functional/model/db/typeconverter/AccountStatusTypeConverter.groovy new file mode 100644 index 00000000000..005aae09d1c --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/db/typeconverter/AccountStatusTypeConverter.groovy @@ -0,0 +1,17 @@ +package org.prebid.server.functional.model.db.typeconverter + +import javax.persistence.AttributeConverter +import org.prebid.server.functional.model.AccountStatus + +class AccountStatusTypeConverter implements AttributeConverter { + + @Override + String convertToDatabaseColumn(AccountStatus accountStatus) { + accountStatus + } + + @Override + AccountStatus convertToEntityAttribute(String value) { + value ? AccountStatus.forValue(value) : null + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/db/typeconverter/ImpConfigTypeConverter.groovy b/src/test/groovy/org/prebid/server/functional/model/db/typeconverter/ImpConfigTypeConverter.groovy new file mode 100644 index 00000000000..4a61adcf038 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/db/typeconverter/ImpConfigTypeConverter.groovy @@ -0,0 +1,18 @@ +package org.prebid.server.functional.model.db.typeconverter + +import javax.persistence.AttributeConverter +import org.prebid.server.functional.model.request.auction.Imp +import org.prebid.server.functional.testcontainers.Dependencies + +class ImpConfigTypeConverter implements AttributeConverter { + + @Override + String convertToDatabaseColumn(Imp imp) { + imp ? Dependencies.objectMapperWrapper.encode(imp) : null + } + + @Override + Imp convertToEntityAttribute(String value) { + value ? Dependencies.objectMapperWrapper.decode(value, Imp) : null + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/db/typeconverter/StoredRequestConfigTypeConverter.groovy b/src/test/groovy/org/prebid/server/functional/model/db/typeconverter/StoredRequestConfigTypeConverter.groovy new file mode 100644 index 00000000000..0201b2b0d4b --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/db/typeconverter/StoredRequestConfigTypeConverter.groovy @@ -0,0 +1,18 @@ +package org.prebid.server.functional.model.db.typeconverter + +import javax.persistence.AttributeConverter +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.testcontainers.Dependencies + +class StoredRequestConfigTypeConverter implements AttributeConverter { + + @Override + String convertToDatabaseColumn(BidRequest bidRequest) { + bidRequest ? Dependencies.objectMapperWrapper.encode(bidRequest) : null + } + + @Override + BidRequest convertToEntityAttribute(String value) { + value ? Dependencies.objectMapperWrapper.decode(value, BidRequest) : null + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/db/typeconverter/StoredResponseConfigTypeConverter.groovy b/src/test/groovy/org/prebid/server/functional/model/db/typeconverter/StoredResponseConfigTypeConverter.groovy new file mode 100644 index 00000000000..21662fe7890 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/db/typeconverter/StoredResponseConfigTypeConverter.groovy @@ -0,0 +1,18 @@ +package org.prebid.server.functional.model.db.typeconverter + +import javax.persistence.AttributeConverter +import org.prebid.server.functional.model.response.auction.BidResponse +import org.prebid.server.functional.testcontainers.Dependencies + +class StoredResponseConfigTypeConverter implements AttributeConverter { + + @Override + String convertToDatabaseColumn(BidResponse bidResponse) { + bidResponse ? Dependencies.objectMapperWrapper.encode(bidResponse) : null + } + + @Override + BidResponse convertToEntityAttribute(String value) { + value ? Dependencies.objectMapperWrapper.decode(value, BidResponse) : null + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/mock/services/httpsettings/HttpAccountsResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/mock/services/httpsettings/HttpAccountsResponse.groovy new file mode 100644 index 00000000000..a30b6146fce --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/mock/services/httpsettings/HttpAccountsResponse.groovy @@ -0,0 +1,25 @@ +package org.prebid.server.functional.model.mock.services.httpsettings + +import groovy.transform.ToString +import org.prebid.server.functional.model.ResponseModel +import org.prebid.server.functional.model.config.AccountAuctionConfig +import org.prebid.server.functional.model.config.AccountConfig +import org.prebid.server.functional.model.config.AccountEventsConfig +import org.prebid.server.functional.model.config.AccountGdprConfig +import org.prebid.server.functional.model.config.AccountPrivacyConfig + +@ToString(includeNames = true, ignoreNulls = true) +class HttpAccountsResponse implements ResponseModel { + + Map accounts + + static HttpAccountsResponse getDefaultHttpAccountsResponse(String accountId) { + def account = new AccountConfig().tap { + id = accountId + auction = new AccountAuctionConfig(events: new AccountEventsConfig(enabled: true)) + privacy = new AccountPrivacyConfig(gdpr: new AccountGdprConfig(enabled: false)) + } + + new HttpAccountsResponse(accounts: [(accountId): account]) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/mock/services/prebidcache/request/PutObject.groovy b/src/test/groovy/org/prebid/server/functional/model/mock/services/prebidcache/request/PutObject.groovy new file mode 100644 index 00000000000..a2930dc8ed5 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/mock/services/prebidcache/request/PutObject.groovy @@ -0,0 +1,28 @@ +package org.prebid.server.functional.model.mock.services.prebidcache.request + +import groovy.transform.ToString +import org.prebid.server.functional.util.PBSUtils + +import static Type.XML +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC + +@ToString(includeNames = true, ignoreNulls = true) +class PutObject { + + Type type + String value + Integer ttlseconds + String bidid + String bidder + Long timestamp + + static PutObject getDefaultPutObject(String creative, Type creativeType = XML) { + new PutObject().tap { + bidid = PBSUtils.randomNumber.toString() + bidder = GENERIC.value + type = creativeType + ttlseconds = 10 + value = creative + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/mock/services/prebidcache/request/Type.groovy b/src/test/groovy/org/prebid/server/functional/model/mock/services/prebidcache/request/Type.groovy new file mode 100644 index 00000000000..f291977ec67 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/mock/services/prebidcache/request/Type.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.mock.services.prebidcache.request + +import com.fasterxml.jackson.annotation.JsonValue + +enum Type { + + XML, JSON + + @JsonValue + String getValue() { + name().toLowerCase() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/mock/services/prebidcache/response/CacheObject.groovy b/src/test/groovy/org/prebid/server/functional/model/mock/services/prebidcache/response/CacheObject.groovy new file mode 100644 index 00000000000..592bae7f00a --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/mock/services/prebidcache/response/CacheObject.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.mock.services.prebidcache.response + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = false) +class CacheObject { + + String uuid + + static CacheObject getDefaultCacheObject() { + new CacheObject(uuid: UUID.randomUUID()) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/mock/services/prebidcache/response/PrebidCacheResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/mock/services/prebidcache/response/PrebidCacheResponse.groovy new file mode 100644 index 00000000000..91205e130ff --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/mock/services/prebidcache/response/PrebidCacheResponse.groovy @@ -0,0 +1,23 @@ +package org.prebid.server.functional.model.mock.services.prebidcache.response + +import groovy.transform.ToString +import org.prebid.server.functional.model.ResponseModel + +@ToString(includeNames = true, ignoreNulls = true) +class PrebidCacheResponse implements ResponseModel { + + List responses + + static PrebidCacheResponse getDefaultCacheResponse() { + def response = new PrebidCacheResponse() + response.addResponse(CacheObject.defaultCacheObject) + response + } + + void addResponse(CacheObject cacheResponse) { + if (this.responses == null) { + this.responses = [] + } + this.responses.add(cacheResponse) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/mock/services/pubstack/EventType.groovy b/src/test/groovy/org/prebid/server/functional/model/mock/services/pubstack/EventType.groovy new file mode 100644 index 00000000000..88195ca616e --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/mock/services/pubstack/EventType.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.mock.services.pubstack + +import com.fasterxml.jackson.annotation.JsonValue + +enum EventType { + + AUCTION, COOKIESYNC, AMP, SETUID, VIDEO + + @JsonValue + String getValue() { + name().toLowerCase() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/mock/services/pubstack/PubStackResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/mock/services/pubstack/PubStackResponse.groovy new file mode 100644 index 00000000000..44f21b586e3 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/mock/services/pubstack/PubStackResponse.groovy @@ -0,0 +1,24 @@ +package org.prebid.server.functional.model.mock.services.pubstack + +import groovy.transform.ToString +import org.prebid.server.functional.model.ResponseModel + +@ToString(includeNames = true, ignoreNulls = true) +class PubStackResponse implements ResponseModel { + + String scopeId + String endpoint + Map features + + static PubStackResponse getDefaultPubStackResponse(String scopeIdValue, String endpointValue) { + new PubStackResponse().tap { + scopeId = scopeIdValue + endpoint = endpointValue + features = allEventTypeEnabled + } + } + + private static Map getAllEventTypeEnabled() { + EventType.values().collectEntries { [it, true] } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/Format.groovy b/src/test/groovy/org/prebid/server/functional/model/request/Format.groovy new file mode 100644 index 00000000000..e9ca3213591 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/Format.groovy @@ -0,0 +1,22 @@ +package org.prebid.server.functional.model.request + +import com.fasterxml.jackson.annotation.JsonValue +import groovy.transform.ToString + +@ToString +enum Format { + + IMAGE("i"), BLANK("b") + + @JsonValue + final String value + + Format(String value) { + this.value = value + } + + @Override + String toString() { + value + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/amp/AmpRequest.groovy b/src/test/groovy/org/prebid/server/functional/model/request/amp/AmpRequest.groovy new file mode 100644 index 00000000000..e7230cd0b81 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/amp/AmpRequest.groovy @@ -0,0 +1,37 @@ +package org.prebid.server.functional.model.request.amp + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString +import org.prebid.server.functional.util.PBSUtils + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy) +class AmpRequest { + + String tagId + String debug + Integer ow + Integer oh + Integer w + Integer h + Long ms + Long timeout + String slot + String curl + Integer account + String gdprConsent + String targeting + Integer consentType + Boolean gdprApplies + String addtlConsent + + static AmpRequest getDefaultAmpRequest() { + def request = new AmpRequest() + request.tagId = PBSUtils.randomString + request.curl = PBSUtils.randomString + request.account = PBSUtils.randomNumber + request.debug = "1" + request + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Amp.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Amp.groovy new file mode 100644 index 00000000000..cbc381ce564 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Amp.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString +import org.prebid.server.functional.model.request.amp.AmpRequest + +@ToString(includeNames = true, ignoreNulls = true) +class Amp { + + AmpRequest data +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/App.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/App.groovy new file mode 100644 index 00000000000..bff2f7e0cd9 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/App.groovy @@ -0,0 +1,27 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString +import org.prebid.server.functional.util.PBSUtils + +@ToString(includeNames = true, ignoreNulls = true) +class App { + + String id + String name + String bundle + String domain + String storeurl + List cat + List sectioncat + List pagecat + String ver + Integer privacypolicy + Integer paid + Publisher publisher + Content content + String keywords + + static App getDefaultApp() { + new App(id: PBSUtils.randomString) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Asset.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Asset.groovy new file mode 100644 index 00000000000..a083b49ade3 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Asset.groovy @@ -0,0 +1,14 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Asset { + + Integer id + Integer required + AssetTitle title + AssetImage img + AssetVideo video + AssetData data +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/AssetData.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/AssetData.groovy new file mode 100644 index 00000000000..f8a10822cda --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/AssetData.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class AssetData { + + Integer type + Integer len +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/AssetImage.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/AssetImage.groovy new file mode 100644 index 00000000000..9406468df2d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/AssetImage.groovy @@ -0,0 +1,14 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class AssetImage { + + Integer type + Integer w + Integer wmin + Integer h + Integer hmin + List mimes +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/AssetTitle.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/AssetTitle.groovy new file mode 100644 index 00000000000..63fc4d01223 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/AssetTitle.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class AssetTitle { + + Integer len +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/AssetVideo.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/AssetVideo.groovy new file mode 100644 index 00000000000..65e43f9658b --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/AssetVideo.groovy @@ -0,0 +1,12 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class AssetVideo { + + List mimes + Integer minduration + Integer maxduration + List protocols +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Audio.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Audio.groovy new file mode 100644 index 00000000000..e61ad3175f7 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Audio.groovy @@ -0,0 +1,26 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Audio { + + List mimes + Integer minduration + Integer maxduration + List protocols + Integer startdelay + Integer sequence + List battr + Integer maxextended + Integer minbitrate + Integer maxbitrate + List delivery + List companionad + List api + List companiontype + Integer maxseq + Integer feed + Integer stitched + Integer nvol +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Banner.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Banner.groovy new file mode 100644 index 00000000000..b9d4faf3e16 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Banner.groovy @@ -0,0 +1,33 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Banner { + + List format + Integer w + Integer h + List btype + List battr + Integer pos + List mimes + Integer topframe + List expdir + List api + String id + Integer vcm + + static Banner getDefaultBanner() { + new Banner().tap { + addFormat(Format.defaultFormat) + } + } + + void addFormat(Format format) { + if (this.format == null) { + this.format = [] + } + this.format.add(format) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRequest.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRequest.groovy new file mode 100644 index 00000000000..07722f4f1ca --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRequest.groovy @@ -0,0 +1,83 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.annotation.JsonIgnore +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString + +@EqualsAndHashCode +@ToString(includeNames = true, ignoreNulls = true) +class BidRequest { + + String id + List imp + Site site + App app + Device device + User user + Integer test + Integer at + Long tmax + List wseat + List bseat + Integer allimps + List cur + List wlang + List bcat + List badv + List bapp + Source source + Regs regs + BidRequestExt ext + + static BidRequest getDefaultBidRequest() { + new BidRequest().tap { + it.addImp(Imp.defaultImpression) + regs = Regs.defaultRegs + id = UUID.randomUUID() + tmax = 2500 + site = Site.defaultSite + ext = new BidRequestExt(prebid: new Prebid(debug: 1)) + } + } + + static BidRequest getDefaultStoredRequest() { + getDefaultBidRequest().tap { + site = null + } + } + + void addImp(Imp impression) { + if (imp == null) { + imp = [] + } + imp.add(impression) + } + + @JsonIgnore + List getRequestBidders() { + def bidderList = [] + def bidder = imp[0]?.ext?.prebid?.bidder + if (bidder) { + bidderList = bidder.configuredBidders + } + bidderList + } + + void enableCache() { + if (ext == null) { + ext = new BidRequestExt() + } + if (ext.prebid == null) { + ext.prebid = new Prebid() + } + if (ext.prebid.cache == null) { + ext.prebid.cache = new PrebidCache() + } + if (ext.prebid.cache.bids == null) { + ext.prebid.cache.bids = new PrebidCacheSettings() + } + if (ext.prebid.cache.vastXml == null) { + ext.prebid.cache.vastXml = new PrebidCacheSettings() + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRequestExt.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRequestExt.groovy new file mode 100644 index 00000000000..460f1382ed4 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRequestExt.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class BidRequestExt { + + Prebid prebid +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Bidder.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Bidder.groovy new file mode 100644 index 00000000000..8e550b5a30d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Bidder.groovy @@ -0,0 +1,29 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.annotation.JsonIgnore +import com.fasterxml.jackson.annotation.JsonProperty +import groovy.transform.ToString +import org.prebid.server.functional.model.bidder.AppNexus +import org.prebid.server.functional.model.bidder.Generic +import org.prebid.server.functional.model.bidder.Rubicon + +@ToString(includeNames = true, ignoreNulls = true) +class Bidder { + + Generic generic + Rubicon rubicon + @JsonProperty("appnexus") + AppNexus appNexus + + static Bidder getDefaultBidder() { + new Bidder().tap { + generic = new Generic() + } + } + + @JsonIgnore + List getConfiguredBidders() { + this.class.declaredFields.findAll { !it.synthetic && this[it.name] != null } + .collect { it.name } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Channel.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Channel.groovy new file mode 100644 index 00000000000..551d94a40c2 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Channel.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Channel { + + String name +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Content.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Content.groovy new file mode 100644 index 00000000000..7a493461712 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Content.groovy @@ -0,0 +1,32 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Content { + + String id + Integer episode + String title + String series + String season + String artist + String genre + String album + String isrc + Producer producer + String url + List cat + Integer prodq + Integer context + String contentrating + String userrating + Integer qagmediarating + String keywords + Integer livestream + Integer sourcerelationship + Integer len + String language + Integer embeddable + List data +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Data.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Data.groovy new file mode 100644 index 00000000000..287d436d5ae --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Data.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Data { + + String id + String name + List segment +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Deal.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Deal.groovy new file mode 100644 index 00000000000..bb8b86ab594 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Deal.groovy @@ -0,0 +1,14 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Deal { + + String id + Float bidfloor + String bidfloorcur + Integer at + List wseat + List wadomain +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Device.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Device.groovy new file mode 100644 index 00000000000..136d0584f53 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Device.groovy @@ -0,0 +1,38 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Device { + + String ua + Geo geo + Integer dnt + Integer lmt + String ip + String ipv6 + Integer devicetype + String make + String model + String os + String osv + String hwv + Integer h + Integer w + Integer ppi + BigDecimal pxratio + Integer js + Integer geofetch + String flashver + String language + String carrier + String mccmnc + Integer connectiontype + String ifa + String didsha1 + String didmd5 + String dpidsha1 + String dpidmd5 + String macsha1 + String macmd5 +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Format.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Format.groovy new file mode 100644 index 00000000000..0f8236481f5 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Format.groovy @@ -0,0 +1,20 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Format { + + Integer w + Integer h + Integer wratio + Integer hratio + Integer wmin + + static Format getDefaultFormat() { + new Format().tap { + w = 300 + h = 250 + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Geo.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Geo.groovy new file mode 100644 index 00000000000..09792638fed --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Geo.groovy @@ -0,0 +1,21 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Geo { + + Float lat + Float lon + Integer type + Integer accuracy + Integer lastfix + Integer ipservice + String country + String region + String regionfips104 + String metro + String city + String zip + Integer utcoffset +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Imp.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Imp.groovy new file mode 100644 index 00000000000..1f2ef62dd68 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Imp.groovy @@ -0,0 +1,43 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.annotation.JsonProperty +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString + +@EqualsAndHashCode +@ToString(includeNames = true, ignoreNulls = true) +class Imp { + + String id + Banner banner + List metric + Video video + Audio audio + @JsonProperty("native") + Native nativeObj + Pmp pmp + String displaymanager + String displaymanagerver + Integer instl + String tagid + BigDecimal bidfloor + String bidfloorcur + Integer clickbrowser + Integer secure + List iframebuster + Integer exp + ImpExt ext + + static Imp getDefaultImpression() { + getDefaultImp().tap { + banner = Banner.getDefaultBanner() + } + } + + private static Imp getDefaultImp() { + new Imp().tap { + id = UUID.randomUUID() + ext = ImpExt.defaultImpExt + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExt.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExt.groovy new file mode 100644 index 00000000000..2d7c9f2c4e1 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExt.groovy @@ -0,0 +1,28 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.annotation.JsonProperty +import groovy.transform.ToString +import org.prebid.server.functional.model.bidder.AppNexus +import org.prebid.server.functional.model.bidder.Generic +import org.prebid.server.functional.model.bidder.Rubicon + +@ToString(includeNames = true, ignoreNulls = true) +class ImpExt { + + ImpExtPrebid prebid + + Generic generic + + @Deprecated + Rubicon rubicon + + @Deprecated + @JsonProperty("appnexus") + AppNexus appNexus + + static ImpExt getDefaultImpExt() { + new ImpExt().tap { + prebid = ImpExtPrebid.defaultImpExtPrebid + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExtPrebid.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExtPrebid.groovy new file mode 100644 index 00000000000..29200bd300b --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExtPrebid.groovy @@ -0,0 +1,15 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class ImpExtPrebid { + + Bidder bidder + + static ImpExtPrebid getDefaultImpExtPrebid() { + new ImpExtPrebid().tap { + bidder = Bidder.defaultBidder + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Metric.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Metric.groovy new file mode 100644 index 00000000000..c936d08973f --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Metric.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Metric { + + String type + Float value + String vendor +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/MultiBid.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/MultiBid.groovy new file mode 100644 index 00000000000..22fd5451629 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/MultiBid.groovy @@ -0,0 +1,15 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = false) +@JsonNaming(PropertyNamingStrategy.LowerCaseStrategy.class) +class MultiBid { + + String bidder + List bidders + Integer maxBids + String targetBidderCodePrefix +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Native.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Native.groovy new file mode 100644 index 00000000000..ea6d50030ed --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Native.groovy @@ -0,0 +1,12 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Native { + + String request + String ver + List api + List battr +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Pbs.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Pbs.groovy new file mode 100644 index 00000000000..cf7f9734bf3 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Pbs.groovy @@ -0,0 +1,6 @@ +package org.prebid.server.functional.model.request.auction + +class Pbs { + + String endpoint +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Pmp.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Pmp.groovy new file mode 100644 index 00000000000..bd5f64fbe82 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Pmp.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Pmp { + + Integer privateAuction + List deals +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy new file mode 100644 index 00000000000..515e760b5cc --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy @@ -0,0 +1,22 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString +import org.prebid.server.functional.model.bidder.BidderName + +@JsonNaming(PropertyNamingStrategy.LowerCaseStrategy.class) +@ToString(includeNames = true, ignoreNulls = true) +class Prebid { + + Integer debug + Targeting targeting + PrebidCache cache + StoredRequest storedRequest + Amp amp + Channel channel + List schains + List multibid + Pbs pbs + Map> bidderParams +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/PrebidCache.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/PrebidCache.groovy new file mode 100644 index 00000000000..d196c8ea775 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/PrebidCache.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.annotation.JsonProperty +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class PrebidCache { + + Boolean winningOnly + PrebidCacheSettings bids + @JsonProperty("vastxml") + PrebidCacheSettings vastXml +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/PrebidCacheSettings.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/PrebidCacheSettings.groovy new file mode 100644 index 00000000000..76f39880e71 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/PrebidCacheSettings.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class PrebidCacheSettings { + + Integer ttlSeconds + Boolean returnCreative +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/PrebidSchain.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/PrebidSchain.groovy new file mode 100644 index 00000000000..5032bac6de3 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/PrebidSchain.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = false) +class PrebidSchain { + + List bidders + Schain schain +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/PriceGranularity.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/PriceGranularity.groovy new file mode 100644 index 00000000000..29f4472cab2 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/PriceGranularity.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class PriceGranularity { + + Integer precision + List ranges +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Producer.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Producer.groovy new file mode 100644 index 00000000000..3882b876dd4 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Producer.groovy @@ -0,0 +1,12 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Producer { + + String id + String name + List cat + String domain +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Publisher.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Publisher.groovy new file mode 100644 index 00000000000..cd0295fe98c --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Publisher.groovy @@ -0,0 +1,19 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString +import org.prebid.server.functional.util.PBSUtils + +@ToString(includeNames = true, ignoreNulls = true) +class Publisher { + + String id + String name + List cat + String domain + + static Publisher getDefaultPublisher() { + new Publisher().tap { + id = PBSUtils.randomNumber.toString() + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Range.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Range.groovy new file mode 100644 index 00000000000..f54034f9f14 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Range.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Range { + + Integer max + BigDecimal increment +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Regs.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Regs.groovy new file mode 100644 index 00000000000..fe7175ef8be --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Regs.groovy @@ -0,0 +1,16 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Regs { + + Integer coppa + RegsExt ext + + static Regs getDefaultRegs() { + new Regs().tap { + ext = new RegsExt(gdpr: 0) + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/RegsExt.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/RegsExt.groovy new file mode 100644 index 00000000000..2647e3ac020 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/RegsExt.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy) +class RegsExt { + + Integer gdpr + String usPrivacy +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Schain.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Schain.groovy new file mode 100644 index 00000000000..8ae7f452c4b --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Schain.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = false) +class Schain { + + String ver + Integer complete + List nodes +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/SchainNode.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/SchainNode.groovy new file mode 100644 index 00000000000..883543b7dd6 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/SchainNode.groovy @@ -0,0 +1,16 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString + +@EqualsAndHashCode +@ToString(includeNames = true, ignoreNulls = false) +class SchainNode { + + String asi + String sid + Integer hp + String rid + String name + String domain +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Segment.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Segment.groovy new file mode 100644 index 00000000000..c6f54e34c2f --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Segment.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = false) +class Segment { + + String id + String name + String value +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Site.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Site.groovy new file mode 100644 index 00000000000..621ebb910a4 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Site.groovy @@ -0,0 +1,31 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString +import org.prebid.server.functional.util.PBSUtils + +@ToString(includeNames = true, ignoreNulls = true) +class Site { + + String id + String name + String domain + List cat + List sectioncat + List pagecat + String page + String ref + String search + Integer mobile + Integer privacypolicy + Publisher publisher + Content content + String keywords + SiteExt ext + + static Site getDefaultSite() { + new Site().tap { + page = PBSUtils.randomString + publisher = Publisher.defaultPublisher + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/SiteExt.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/SiteExt.groovy new file mode 100644 index 00000000000..2a9f35a6d44 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/SiteExt.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class SiteExt { + + Integer amp +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Source.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Source.groovy new file mode 100644 index 00000000000..b3496024265 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Source.groovy @@ -0,0 +1,12 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Source { + + Integer fd + String tid + String pchain + SourceExt ext +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/SourceExt.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/SourceExt.groovy new file mode 100644 index 00000000000..469130a0cc7 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/SourceExt.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = false) +class SourceExt { + + Schain schain +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/StoredRequest.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/StoredRequest.groovy new file mode 100644 index 00000000000..d392a3ab817 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/StoredRequest.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class StoredRequest { + + String id +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Targeting.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Targeting.groovy new file mode 100644 index 00000000000..b27c0e9c267 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Targeting.groovy @@ -0,0 +1,15 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.LowerCaseStrategy.class) +class Targeting { + + PriceGranularity priceGranularity + Boolean includeWinners + Boolean includeBidderKeys + Boolean preferdeals +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/User.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/User.groovy new file mode 100644 index 00000000000..8df6e8039d6 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/User.groovy @@ -0,0 +1,18 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class User { + + String id + String buyeruid + Integer yob + String gender + String language + String keywords + String customdata + Geo geo + List data + UserExt ext +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/UserExt.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/UserExt.groovy new file mode 100644 index 00000000000..79b4b7b8c38 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/UserExt.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class UserExt { + + String consent +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Video.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Video.groovy new file mode 100644 index 00000000000..2c604d3b7ad --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Video.groovy @@ -0,0 +1,33 @@ +package org.prebid.server.functional.model.request.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class Video { + + List mimes + Integer minduration + Integer maxduration + List protocols + Integer w + Integer h + Integer startdelay + Integer placement + Integer linearity + Integer skip + Integer skipmin + Integer skipafter + Integer sequence + List battr + Integer maxextended + Integer minbitrate + Integer maxbitrate + Integer boxingallowed + List playbackmethod + Integer playbackend + List delivery + Integer pos + List companionad + List api + List companiontype +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/cookiesync/CookieSyncRequest.groovy b/src/test/groovy/org/prebid/server/functional/model/request/cookiesync/CookieSyncRequest.groovy new file mode 100644 index 00000000000..df3e4858fb6 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/cookiesync/CookieSyncRequest.groovy @@ -0,0 +1,30 @@ +package org.prebid.server.functional.model.request.cookiesync + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString +import org.prebid.server.functional.model.bidder.BidderName + +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy) +class CookieSyncRequest { + + List bidders + Integer gdpr + String gdprConsent + String usPrivacy + @JsonProperty("coopSync") + Boolean coopSync + Integer limit + String account + + static CookieSyncRequest getDefaultCookieSyncRequest() { + def request = new CookieSyncRequest() + request.bidders = [GENERIC] + request.gdpr = 0 + request + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/event/EventRequest.groovy b/src/test/groovy/org/prebid/server/functional/model/request/event/EventRequest.groovy new file mode 100644 index 00000000000..6ed94f08161 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/event/EventRequest.groovy @@ -0,0 +1,38 @@ +package org.prebid.server.functional.model.request.event + +import com.fasterxml.jackson.annotation.JsonProperty +import groovy.transform.ToString +import org.prebid.server.functional.model.request.Format +import org.prebid.server.functional.util.PBSUtils + +import static org.prebid.server.functional.model.request.Format.IMAGE +import static org.prebid.server.functional.model.request.event.EventType.WIN + +@ToString(includeNames = true, ignoreNulls = true) +class EventRequest { + + @JsonProperty("t") + EventType type + @JsonProperty("b") + String bidId + @JsonProperty("a") + Integer accountId + @JsonProperty("f") + Format format + String bidder + @JsonProperty("x") + Integer analytics + @JsonProperty("ts") + Long timestamp + + static EventRequest getDefaultEventRequest() { + def request = new EventRequest() + request.type = WIN + request.bidId = PBSUtils.randomString + request.accountId = PBSUtils.randomNumber + request.format = IMAGE + request.bidder = "generic" + request.timestamp = System.currentTimeMillis() + request + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/event/EventType.groovy b/src/test/groovy/org/prebid/server/functional/model/request/event/EventType.groovy new file mode 100644 index 00000000000..841c13e0843 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/event/EventType.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.request.event + +import com.fasterxml.jackson.annotation.JsonValue + +enum EventType { + + WIN, IMP + + @JsonValue + String getValue() { + name().toLowerCase() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/logging/httpinteraction/HttpInteractionRequest.groovy b/src/test/groovy/org/prebid/server/functional/model/request/logging/httpinteraction/HttpInteractionRequest.groovy new file mode 100644 index 00000000000..d4a4c3313cb --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/logging/httpinteraction/HttpInteractionRequest.groovy @@ -0,0 +1,20 @@ +package org.prebid.server.functional.model.request.logging.httpinteraction + +import groovy.transform.ToString +import org.prebid.server.functional.model.bidder.BidderName + +@ToString(includeNames = true, ignoreNulls = true) +class HttpInteractionRequest { + + String endpoint + String statusCode + String account + Integer limit + BidderName bidder + + static HttpInteractionRequest getDefaultHttpInteractionRequest() { + def request = new HttpInteractionRequest() + request.limit = 1 // Using this number as there is no point in capturing more requests by default + request + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/setuid/SetuidRequest.groovy b/src/test/groovy/org/prebid/server/functional/model/request/setuid/SetuidRequest.groovy new file mode 100644 index 00000000000..cb6bccb8495 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/setuid/SetuidRequest.groovy @@ -0,0 +1,28 @@ +package org.prebid.server.functional.model.request.setuid + +import com.fasterxml.jackson.annotation.JsonProperty +import groovy.transform.ToString +import org.prebid.server.functional.model.bidder.BidderName +import org.prebid.server.functional.model.request.Format + +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC + +@ToString(includeNames = true, ignoreNulls = true) +class SetuidRequest { + + BidderName bidder + String uid + String gdpr + @JsonProperty("gdpr_consent") + String gdprConsent + @JsonProperty("f") + Format format + String account + + static SetuidRequest getDefaultSetuidRequest() { + def request = new SetuidRequest() + request.bidder = GENERIC + request.gdpr = "0" + request + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/setuid/UidWithExpiry.groovy b/src/test/groovy/org/prebid/server/functional/model/request/setuid/UidWithExpiry.groovy new file mode 100644 index 00000000000..6cd6b83f689 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/setuid/UidWithExpiry.groovy @@ -0,0 +1,14 @@ +package org.prebid.server.functional.model.request.setuid + +import com.fasterxml.jackson.annotation.JsonFormat +import groovy.transform.ToString + +import java.time.ZonedDateTime + +@ToString(includeNames = true, ignoreNulls = true) +class UidWithExpiry { + + String uid + @JsonFormat(pattern = "yyyy-MM-dd'T'hh:mm:ss.SSS'Z'", timezone = "UTC") + ZonedDateTime expires +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/setuid/UidsCookie.groovy b/src/test/groovy/org/prebid/server/functional/model/request/setuid/UidsCookie.groovy new file mode 100644 index 00000000000..4897eb7b672 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/setuid/UidsCookie.groovy @@ -0,0 +1,25 @@ +package org.prebid.server.functional.model.request.setuid + +import com.fasterxml.jackson.annotation.JsonFormat +import groovy.transform.ToString + +import java.time.Clock +import java.time.ZonedDateTime + +@ToString(includeNames = true, ignoreNulls = true) +class UidsCookie { + + Map uids + Map tempUIDs + Boolean optout + @JsonFormat(pattern = "yyyy-MM-dd'T'hh:mm:ss.SSS'Z'", timezone = "UTC") + ZonedDateTime bday + + static UidsCookie getDefaultUidsCookie() { + def uidsCookie = new UidsCookie() + uidsCookie.uids = [generic: UUID.randomUUID().toString()] + uidsCookie.bday = ZonedDateTime.now(Clock.systemUTC()) + uidsCookie.tempUIDs = ["generic": new UidWithExpiry(uid: UUID.randomUUID().toString(), expires: ZonedDateTime.now(Clock.systemUTC()).plusDays(2))] + uidsCookie + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/vtrack/VtrackRequest.groovy b/src/test/groovy/org/prebid/server/functional/model/request/vtrack/VtrackRequest.groovy new file mode 100644 index 00000000000..03f7b8e01ba --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/vtrack/VtrackRequest.groovy @@ -0,0 +1,23 @@ +package org.prebid.server.functional.model.request.vtrack + +import groovy.transform.ToString +import org.prebid.server.functional.model.mock.services.prebidcache.request.PutObject + +@ToString(includeNames = true, ignoreNulls = true) +class VtrackRequest { + + List puts + + static VtrackRequest getDefaultVtrackRequest(String creative) { + def vtrack = new VtrackRequest() + vtrack.addPutObject(PutObject.getDefaultPutObject(creative)) + vtrack + } + + void addPutObject(PutObject putObject) { + if (this.puts == null) { + this.puts = [] + } + this.puts.add(putObject) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/vtrack/xml/Ad.groovy b/src/test/groovy/org/prebid/server/functional/model/request/vtrack/xml/Ad.groovy new file mode 100644 index 00000000000..9eec43677fe --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/vtrack/xml/Ad.groovy @@ -0,0 +1,15 @@ +package org.prebid.server.functional.model.request.vtrack.xml + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty + +class Ad { + + @JacksonXmlProperty(localName = "Wrapper") + Wrapper wrapper + + static Ad getDefaultAd(String payload) { + new Ad().tap { + wrapper = Wrapper.getDefaultWrapper(payload) + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/vtrack/xml/Vast.groovy b/src/test/groovy/org/prebid/server/functional/model/request/vtrack/xml/Vast.groovy new file mode 100644 index 00000000000..46043571e40 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/vtrack/xml/Vast.groovy @@ -0,0 +1,20 @@ +package org.prebid.server.functional.model.request.vtrack.xml + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement + +@JacksonXmlRootElement(localName = "VAST") +class Vast { + + @JacksonXmlProperty(isAttribute = true) + String version + @JacksonXmlProperty(localName = "Ad") + Ad ad + + static Vast getDefaultVastModel(String payload) { + new Vast().tap { + version = "3.0" + ad = Ad.getDefaultAd(payload) + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/vtrack/xml/Wrapper.groovy b/src/test/groovy/org/prebid/server/functional/model/request/vtrack/xml/Wrapper.groovy new file mode 100644 index 00000000000..d3dff6da36a --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/vtrack/xml/Wrapper.groovy @@ -0,0 +1,24 @@ +package org.prebid.server.functional.model.request.vtrack.xml + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty + +class Wrapper { + + @JacksonXmlProperty(localName = "AdSystem") + String adSystem + @JacksonXmlProperty(localName = "VASTAdTagURI") + String vastAdTagUri + @JacksonXmlProperty(localName = "Impression") + String impression + @JacksonXmlProperty(localName = "Creatives") + String creatives + + static Wrapper getDefaultWrapper(String payload) { + new Wrapper().tap { + adSystem = "prebid.org wrapper" + vastAdTagUri = "" + impression = " " + creatives = "" + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/BidderError.groovy b/src/test/groovy/org/prebid/server/functional/model/response/BidderError.groovy new file mode 100644 index 00000000000..28fe2a7ef95 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/BidderError.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.response + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class BidderError { + + Integer code + String message +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/Debug.groovy b/src/test/groovy/org/prebid/server/functional/model/response/Debug.groovy new file mode 100644 index 00000000000..925b20a0322 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/Debug.groovy @@ -0,0 +1,17 @@ +package org.prebid.server.functional.model.response + +import groovy.transform.ToString +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.response.auction.HttpCall + +@ToString(includeNames = true, ignoreNulls = true) +class Debug { + + Map> httpcalls + BidRequest resolvedrequest + + Map> getBidders() { + def result = httpcalls?.findAll { it.key != "cache" } + result ?: [:] + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/amp/AmpResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/response/amp/AmpResponse.groovy new file mode 100644 index 00000000000..1acb00df6fb --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/amp/AmpResponse.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.response.amp + +import org.prebid.server.functional.model.response.BidderError +import org.prebid.server.functional.model.response.Debug + +class AmpResponse { + + Map targeting + Debug debug + Map> errors +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/Bid.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/Bid.groovy new file mode 100644 index 00000000000..ebcc645a512 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/Bid.groovy @@ -0,0 +1,48 @@ +package org.prebid.server.functional.model.response.auction + +import groovy.transform.ToString +import org.prebid.server.functional.model.request.auction.Imp + +@ToString(includeNames = true, ignoreNulls = true) +class Bid { + + String id + String impid + BigDecimal price + String nurl + String burl + String lurl + String adm + String adid + List adomain + String bundle + String iurl + String cid + String crid + List cat + List attr + Integer api + Integer protocol + Integer qagmediarating + String language + String dealid + Integer w + Integer h + Integer wratio + Integer hratio + Integer exp + BidExt ext + + static Bid getDefaultBid(Imp imp) { + getDefaultBid(imp.id) + } + + static Bid getDefaultBid(String impId) { + new Bid().tap { + id = UUID.randomUUID() + impid = impId + price = 1.23 + crid = 1 + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/BidExt.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidExt.groovy new file mode 100644 index 00000000000..a28236c2873 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidExt.groovy @@ -0,0 +1,7 @@ +package org.prebid.server.functional.model.response.auction + +class BidExt { + + Prebid prebid + BigDecimal origbidcpm +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponse.groovy new file mode 100644 index 00000000000..2398e99d576 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponse.groovy @@ -0,0 +1,35 @@ +package org.prebid.server.functional.model.response.auction + +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString +import org.prebid.server.functional.model.ResponseModel +import org.prebid.server.functional.model.request.auction.BidRequest + +@EqualsAndHashCode +@ToString(includeNames = true, ignoreNulls = true) +class BidResponse implements ResponseModel { + + String id + List seatbid + String bidid + String cur + String customdata + Integer nbr + BidResponseExt ext + + static BidResponse getDefaultBidResponse(BidRequest bidRequest) { + getDefaultBidResponse(bidRequest.id, bidRequest.imp*.id) + } + + static BidResponse getDefaultBidResponse(String id, List impIds) { + def bidResponse = new BidResponse(id: id) + def bids = getDefaultBids(impIds) + def seatBid = new SeatBid(bid: bids) + bidResponse.seatbid = [seatBid] + bidResponse + } + + static private List getDefaultBids(List impIds) { + impIds.collect { Bid.getDefaultBid(it) } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponseExt.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponseExt.groovy new file mode 100644 index 00000000000..0363158d750 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponseExt.groovy @@ -0,0 +1,17 @@ +package org.prebid.server.functional.model.response.auction + +import groovy.transform.ToString +import org.prebid.server.functional.model.response.BidderError +import org.prebid.server.functional.model.response.Debug + +@ToString(includeNames = true, ignoreNulls = true) +class BidResponseExt { + + Debug debug + Map> errors + Map responsetimemillis + Long tmaxrequest + Map usersync + BidResponsePrebid prebid + Map> warnings +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponsePrebid.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponsePrebid.groovy new file mode 100644 index 00000000000..9f398367a84 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponsePrebid.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.response.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class BidResponsePrebid { + + Long auctiontimestamp +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/ErrorType.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/ErrorType.groovy new file mode 100644 index 00000000000..d332b378277 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/ErrorType.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.response.auction + +import com.fasterxml.jackson.annotation.JsonValue + +enum ErrorType { + + GENERAL, GENERIC, RUBICON, APPNEXUS, PREBID + + @JsonValue + String getValue() { + name().toLowerCase() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/HttpCall.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/HttpCall.groovy new file mode 100644 index 00000000000..0df682648f3 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/HttpCall.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.response.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class HttpCall { + + String uri + String requestbody + Map> requestheaders + String responsebody + Integer status +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/MediaType.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/MediaType.groovy new file mode 100644 index 00000000000..8af36d5ec69 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/MediaType.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.response.auction + +import com.fasterxml.jackson.annotation.JsonValue + +enum MediaType { + + BANNER, VIDEO + + @JsonValue + String getValue() { + name().toLowerCase() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/Prebid.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/Prebid.groovy new file mode 100644 index 00000000000..143582391f9 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/Prebid.groovy @@ -0,0 +1,8 @@ +package org.prebid.server.functional.model.response.auction + +class Prebid { + + MediaType type + Map targeting + String targetbiddercode +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/ResponseSyncData.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/ResponseSyncData.groovy new file mode 100644 index 00000000000..788c1793577 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/ResponseSyncData.groovy @@ -0,0 +1,22 @@ +package org.prebid.server.functional.model.response.auction + +import com.fasterxml.jackson.annotation.JsonValue +import groovy.transform.ToString +import org.prebid.server.functional.model.response.cookiesync.UserSync + +@ToString(includeNames = true, ignoreNulls = true) +class ResponseSyncData { + + CookieStatus status + List syncs + + enum CookieStatus { + + NONE, EXPIRED, AVAILABLE + + @JsonValue + String getValue() { + name().toLowerCase() + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/SeatBid.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/SeatBid.groovy new file mode 100644 index 00000000000..26f16db5443 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/SeatBid.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.response.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class SeatBid { + + List bid + String seat + Integer group +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/WarningEntry.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/WarningEntry.groovy new file mode 100644 index 00000000000..0e53914fdad --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/WarningEntry.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.response.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class WarningEntry { + + Integer code + String message +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/biddersparams/BidderParams.groovy b/src/test/groovy/org/prebid/server/functional/model/response/biddersparams/BidderParams.groovy new file mode 100644 index 00000000000..140f25c410f --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/biddersparams/BidderParams.groovy @@ -0,0 +1,20 @@ +package org.prebid.server.functional.model.response.biddersparams + +import com.fasterxml.jackson.annotation.JsonProperty + +class BidderParams { + + @JsonProperty("\$schema") + String schema + String title + String description + String type + def properties + List oneOf + List required + def not + def anyOf + def appid + def placementid + def dependencies +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/biddersparams/BiddersParamsResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/response/biddersparams/BiddersParamsResponse.groovy new file mode 100644 index 00000000000..d1608c1c7e9 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/biddersparams/BiddersParamsResponse.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.response.biddersparams + +import com.fasterxml.jackson.annotation.JsonAnySetter + +class BiddersParamsResponse { + + @JsonAnySetter + Map parameters = [:] +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/biddersparams/OneOf.groovy b/src/test/groovy/org/prebid/server/functional/model/response/biddersparams/OneOf.groovy new file mode 100644 index 00000000000..905a8766b19 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/biddersparams/OneOf.groovy @@ -0,0 +1,7 @@ +package org.prebid.server.functional.model.response.biddersparams + +class OneOf { + + List required + def oneOf +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/cookiesync/BidderUsersyncStatus.groovy b/src/test/groovy/org/prebid/server/functional/model/response/cookiesync/BidderUsersyncStatus.groovy new file mode 100644 index 00000000000..c2f12b18548 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/cookiesync/BidderUsersyncStatus.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.response.cookiesync + +import groovy.transform.ToString +import org.prebid.server.functional.model.bidder.BidderName + +@ToString(includeNames = true, ignoreNulls = true) +class BidderUsersyncStatus { + + BidderName bidder + String error + Boolean no_cookie + UsersyncInfo usersync +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/cookiesync/CookieSyncResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/response/cookiesync/CookieSyncResponse.groovy new file mode 100644 index 00000000000..c85fb573be9 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/cookiesync/CookieSyncResponse.groovy @@ -0,0 +1,28 @@ +package org.prebid.server.functional.model.response.cookiesync + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonValue +import groovy.transform.ToString +import org.prebid.server.functional.model.bidder.BidderName + +@ToString(includeNames = true, ignoreNulls = true) +class CookieSyncResponse { + + Status status + @JsonProperty("bidder_status") + List bidderStatus + + BidderUsersyncStatus getBidderUsersync(BidderName bidderName) { + bidderStatus?.find { it.bidder == bidderName } + } + + enum Status { + + OK, NO_COOKIE + + @JsonValue + String getValue() { + name().toLowerCase() + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/cookiesync/UserSync.groovy b/src/test/groovy/org/prebid/server/functional/model/response/cookiesync/UserSync.groovy new file mode 100644 index 00000000000..76822fe2e57 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/cookiesync/UserSync.groovy @@ -0,0 +1,21 @@ +package org.prebid.server.functional.model.response.cookiesync + +import com.fasterxml.jackson.annotation.JsonValue +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class UserSync { + + String url + UserSyncType type + + enum UserSyncType { + + IFRAME, PIXEL + + @JsonValue + String getValue() { + name().toLowerCase() + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/cookiesync/UsersyncInfo.groovy b/src/test/groovy/org/prebid/server/functional/model/response/cookiesync/UsersyncInfo.groovy new file mode 100644 index 00000000000..766f121c172 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/cookiesync/UsersyncInfo.groovy @@ -0,0 +1,22 @@ +package org.prebid.server.functional.model.response.cookiesync + +import com.fasterxml.jackson.annotation.JsonValue +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class UsersyncInfo { + + String url + Type type + Boolean supportCORS + + enum Type { + + REDIRECT, IFRAME + + @JsonValue + String getValue() { + name().toLowerCase() + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/currencyrates/CurrencyRatesResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/response/currencyrates/CurrencyRatesResponse.groovy new file mode 100644 index 00000000000..7523253b340 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/currencyrates/CurrencyRatesResponse.groovy @@ -0,0 +1,17 @@ +package org.prebid.server.functional.model.response.currencyrates + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class CurrencyRatesResponse { + + Boolean active + + String source + + Long fetchingIntervalNs + + String lastUpdated + + Map> rates +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/getuids/GetuidResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/response/getuids/GetuidResponse.groovy new file mode 100644 index 00000000000..d86ed5a76a3 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/getuids/GetuidResponse.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.response.getuids + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class GetuidResponse { + + Map buyeruids +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/infobidders/BidderInfoResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/response/infobidders/BidderInfoResponse.groovy new file mode 100644 index 00000000000..1edb7bc8006 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/infobidders/BidderInfoResponse.groovy @@ -0,0 +1,18 @@ +package org.prebid.server.functional.model.response.infobidders + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class BidderInfoResponse { + + BidderStatus status + Boolean usesHttps + MaintainerInfo maintainer + Map capabilities + String aliasOf + + enum BidderStatus { + + ACTIVE, DISABLED + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/infobidders/GdprInfo.groovy b/src/test/groovy/org/prebid/server/functional/model/response/infobidders/GdprInfo.groovy new file mode 100644 index 00000000000..eaf76954026 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/infobidders/GdprInfo.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.response.infobidders + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class GdprInfo { + + Integer vendorId + Boolean enforced +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/infobidders/MaintainerInfo.groovy b/src/test/groovy/org/prebid/server/functional/model/response/infobidders/MaintainerInfo.groovy new file mode 100644 index 00000000000..728558fc0e9 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/infobidders/MaintainerInfo.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.response.infobidders + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class MaintainerInfo { + + String email +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/infobidders/PlatformInfo.groovy b/src/test/groovy/org/prebid/server/functional/model/response/infobidders/PlatformInfo.groovy new file mode 100644 index 00000000000..604c966f1eb --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/infobidders/PlatformInfo.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.response.infobidders + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class PlatformInfo { + + List mediaTypes +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/setuid/SetuidResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/response/setuid/SetuidResponse.groovy new file mode 100644 index 00000000000..3bef4783280 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/setuid/SetuidResponse.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.response.setuid + +import groovy.transform.ToString +import io.restassured.http.Cookie + +@ToString(includeNames = true, ignoreNulls = true) +class SetuidResponse { + + Cookie uidsCookie + String responseBody +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/status/ApplicationStatus.groovy b/src/test/groovy/org/prebid/server/functional/model/response/status/ApplicationStatus.groovy new file mode 100644 index 00000000000..592a819e5cd --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/status/ApplicationStatus.groovy @@ -0,0 +1,9 @@ +package org.prebid.server.functional.model.response.status + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class ApplicationStatus { + + Status status +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/status/Status.groovy b/src/test/groovy/org/prebid/server/functional/model/response/status/Status.groovy new file mode 100644 index 00000000000..52ac841f6fe --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/status/Status.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.response.status + +import com.fasterxml.jackson.annotation.JsonValue + +enum Status { + + OK + + @JsonValue + String getValue() { + name().toLowerCase() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/status/StatusResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/response/status/StatusResponse.groovy new file mode 100644 index 00000000000..177c80375b7 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/status/StatusResponse.groovy @@ -0,0 +1,6 @@ +package org.prebid.server.functional.model.response.status + +class StatusResponse { + + ApplicationStatus application +} diff --git a/src/test/groovy/org/prebid/server/functional/repository/EntityManagerUtil.groovy b/src/test/groovy/org/prebid/server/functional/repository/EntityManagerUtil.groovy new file mode 100644 index 00000000000..a7712d6430f --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/repository/EntityManagerUtil.groovy @@ -0,0 +1,52 @@ +package org.prebid.server.functional.repository + +import javax.persistence.EntityManager +import org.hibernate.SessionFactory + +import java.util.function.Consumer +import java.util.function.Function + +class EntityManagerUtil { + + private SessionFactory sessionFactory + + EntityManagerUtil(SessionFactory sessionFactory) { + this.sessionFactory = sessionFactory + } + + void performWithinTx(Consumer entityManagerConsumer) { + EntityManager entityManager = sessionFactory.createEntityManager() + entityManager.transaction.begin() + try { + entityManagerConsumer.accept(entityManager) + entityManager.transaction.commit() + } catch (all) { + entityManager.transaction.rollback() + throw new DaoException("Error performing dao operation. Transaction is rolled back!", all) + } finally { + entityManager.close() + } + } + + def T getResultWithinTx(Function entityManagerFunction) { + EntityManager entityManager = sessionFactory.createEntityManager() + entityManager.transaction.begin() + try { + T result = entityManagerFunction.apply(entityManager) + entityManager.transaction.commit() + return result + } catch (all) { + entityManager.transaction.rollback() + throw new DaoException("Error performing dao operation. Transaction is rolled back!", all) + } finally { + entityManager.close() + } + } + + class DaoException extends RuntimeException { + + DaoException(String message, Throwable cause) { + super(message, cause) + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/repository/HibernateRepositoryService.groovy b/src/test/groovy/org/prebid/server/functional/repository/HibernateRepositoryService.groovy new file mode 100644 index 00000000000..298f3c48773 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/repository/HibernateRepositoryService.groovy @@ -0,0 +1,69 @@ +package org.prebid.server.functional.repository + +import org.hibernate.SessionFactory +import org.hibernate.cfg.Configuration +import org.prebid.server.functional.model.db.Account +import org.prebid.server.functional.model.db.S2sConfig +import org.prebid.server.functional.model.db.StoredImp +import org.prebid.server.functional.model.db.StoredRequest +import org.prebid.server.functional.model.db.StoredResponse +import org.prebid.server.functional.repository.dao.AccountDao +import org.prebid.server.functional.repository.dao.ConfigDao +import org.prebid.server.functional.repository.dao.StoredImpDao +import org.prebid.server.functional.repository.dao.StoredRequestDao +import org.prebid.server.functional.repository.dao.StoredResponseDao +import org.testcontainers.containers.MySQLContainer + +class HibernateRepositoryService { + + EntityManagerUtil entityManagerUtil + AccountDao accountDao + ConfigDao configDao + StoredImpDao storedImpDao + StoredRequestDao storedRequestDao + StoredResponseDao storedResponseDao + + HibernateRepositoryService(MySQLContainer container) { + def jdbcUrl = container.jdbcUrl + def user = container.username + def pass = container.password + def driver = container.driverClassName + SessionFactory sessionFactory = configureHibernate(jdbcUrl, user, pass, driver) + entityManagerUtil = new EntityManagerUtil(sessionFactory) + + accountDao = new AccountDao(entityManagerUtil) + configDao = new ConfigDao(entityManagerUtil) + storedImpDao = new StoredImpDao(entityManagerUtil) + storedRequestDao = new StoredRequestDao(entityManagerUtil) + storedResponseDao = new StoredResponseDao(entityManagerUtil) + } + + private static SessionFactory configureHibernate(String jdbcUrl, String user, String pass, String driver) { + def properties = new Properties() + properties.setProperty("hibernate.connection.url", jdbcUrl) + properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect") + properties.setProperty("hibernate.connection.username", user) + properties.setProperty("hibernate.connection.password", pass) + properties.setProperty("hibernate.connection.driver_class", driver) + properties.setProperty("hibernate.show_sql", "false") + properties.setProperty("hibernate.format_sql", "false") + + def configuration = new Configuration() + configuration.addAnnotatedClass(Account) + configuration.addAnnotatedClass(S2sConfig) + configuration.addAnnotatedClass(StoredImp) + configuration.addAnnotatedClass(StoredRequest) + configuration.addAnnotatedClass(StoredResponse) + + SessionFactory sessionFactory = configuration.addProperties(properties).buildSessionFactory() + sessionFactory + } + + void removeAllDatabaseData() { + accountDao.removeAll() + configDao.removeAll() + storedImpDao.removeAll() + storedRequestDao.removeAll() + storedResponseDao.removeAll() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/repository/dao/AccountDao.groovy b/src/test/groovy/org/prebid/server/functional/repository/dao/AccountDao.groovy new file mode 100644 index 00000000000..7a7bc2650a0 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/repository/dao/AccountDao.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.repository.dao + +import org.prebid.server.functional.model.db.Account +import org.prebid.server.functional.repository.EntityManagerUtil + +class AccountDao extends EntityDao { + + AccountDao(EntityManagerUtil entityManagerUtil) { + super(entityManagerUtil, Account) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/repository/dao/ConfigDao.groovy b/src/test/groovy/org/prebid/server/functional/repository/dao/ConfigDao.groovy new file mode 100644 index 00000000000..1d1055605ab --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/repository/dao/ConfigDao.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.repository.dao + +import org.prebid.server.functional.model.db.S2sConfig +import org.prebid.server.functional.repository.EntityManagerUtil + +class ConfigDao extends EntityDao { + + ConfigDao(EntityManagerUtil entityManagerUtil) { + super(entityManagerUtil, S2sConfig) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/repository/dao/EntityDao.groovy b/src/test/groovy/org/prebid/server/functional/repository/dao/EntityDao.groovy new file mode 100644 index 00000000000..39f61daf3cb --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/repository/dao/EntityDao.groovy @@ -0,0 +1,45 @@ +package org.prebid.server.functional.repository.dao + +import org.prebid.server.functional.repository.EntityManagerUtil + +abstract class EntityDao { + + EntityManagerUtil emUtil + + Class entityClass + String entityClassName + + EntityDao(EntityManagerUtil entityManagerUtil, Class entityClass) { + this.emUtil = entityManagerUtil + this.entityClass = entityClass + this.entityClassName = entityClass.simpleName + } + + ENTITY update(ENTITY entity) { + emUtil.getResultWithinTx { it.merge(entity) } + } + + ENTITY save(ENTITY entity) { + emUtil.performWithinTx { it.persist(entity) } + entity + } + + ENTITY findById(ID id) { + emUtil.getResultWithinTx { it.find(entityClass, id) } + } + + List findAll() { + emUtil.getResultWithinTx { it.createQuery("SELECT e FROM $entityClassName e").resultList } + } + + void remove(ENTITY entity) { + emUtil.performWithinTx { + ENTITY managedEntity = it.merge(entity) + it.remove(managedEntity) + } + } + + void removeAll() { + emUtil.performWithinTx { it.createQuery("DELETE FROM $entityClassName").executeUpdate() } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/repository/dao/StoredImpDao.groovy b/src/test/groovy/org/prebid/server/functional/repository/dao/StoredImpDao.groovy new file mode 100644 index 00000000000..a382dc6d45b --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/repository/dao/StoredImpDao.groovy @@ -0,0 +1,12 @@ +package org.prebid.server.functional.repository.dao + +import org.prebid.server.functional.model.db.StoredImp +import org.prebid.server.functional.repository.EntityManagerUtil + +class StoredImpDao extends EntityDao { + + StoredImpDao(EntityManagerUtil entityManagerUtil) { + super(entityManagerUtil, StoredImp) + } + +} diff --git a/src/test/groovy/org/prebid/server/functional/repository/dao/StoredRequestDao.groovy b/src/test/groovy/org/prebid/server/functional/repository/dao/StoredRequestDao.groovy new file mode 100644 index 00000000000..78a5e7aa40b --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/repository/dao/StoredRequestDao.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.repository.dao + +import org.prebid.server.functional.model.db.StoredRequest +import org.prebid.server.functional.repository.EntityManagerUtil + +class StoredRequestDao extends EntityDao { + + StoredRequestDao(EntityManagerUtil entityManagerUtil) { + super(entityManagerUtil, StoredRequest) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/repository/dao/StoredResponseDao.groovy b/src/test/groovy/org/prebid/server/functional/repository/dao/StoredResponseDao.groovy new file mode 100644 index 00000000000..bd9a784f97d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/repository/dao/StoredResponseDao.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.repository.dao + +import org.prebid.server.functional.model.db.StoredResponse +import org.prebid.server.functional.repository.EntityManagerUtil + +class StoredResponseDao extends EntityDao { + + StoredResponseDao(EntityManagerUtil entityManagerUtil) { + super(entityManagerUtil, StoredResponse) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/service/PrebidServerException.groovy b/src/test/groovy/org/prebid/server/functional/service/PrebidServerException.groovy new file mode 100644 index 00000000000..1f62ccf2936 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/service/PrebidServerException.groovy @@ -0,0 +1,12 @@ +package org.prebid.server.functional.service + +class PrebidServerException extends Exception { + + final int statusCode + final String responseBody + + PrebidServerException(int statusCode, String message) { + this.statusCode = statusCode + this.responseBody = message + } +} diff --git a/src/test/groovy/org/prebid/server/functional/service/PrebidServerService.groovy b/src/test/groovy/org/prebid/server/functional/service/PrebidServerService.groovy new file mode 100644 index 00000000000..3a554d710ea --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/service/PrebidServerService.groovy @@ -0,0 +1,275 @@ +package org.prebid.server.functional.service + +import com.fasterxml.jackson.core.type.TypeReference +import io.qameta.allure.Step +import io.restassured.RestAssured +import io.restassured.authentication.BasicAuthScheme +import io.restassured.builder.RequestSpecBuilder +import io.restassured.response.Response +import io.restassured.specification.RequestSpecification +import org.prebid.server.functional.model.bidder.BidderName +import org.prebid.server.functional.model.mock.services.prebidcache.response.PrebidCacheResponse +import org.prebid.server.functional.model.request.amp.AmpRequest +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.cookiesync.CookieSyncRequest +import org.prebid.server.functional.model.request.event.EventRequest +import org.prebid.server.functional.model.request.logging.httpinteraction.HttpInteractionRequest +import org.prebid.server.functional.model.request.setuid.SetuidRequest +import org.prebid.server.functional.model.request.setuid.UidsCookie +import org.prebid.server.functional.model.request.vtrack.VtrackRequest +import org.prebid.server.functional.model.response.amp.AmpResponse +import org.prebid.server.functional.model.response.auction.BidResponse +import org.prebid.server.functional.model.response.biddersparams.BiddersParamsResponse +import org.prebid.server.functional.model.response.cookiesync.CookieSyncResponse +import org.prebid.server.functional.model.response.currencyrates.CurrencyRatesResponse +import org.prebid.server.functional.model.response.getuids.GetuidResponse +import org.prebid.server.functional.model.response.infobidders.BidderInfoResponse +import org.prebid.server.functional.model.response.setuid.SetuidResponse +import org.prebid.server.functional.model.response.status.StatusResponse +import org.prebid.server.functional.testcontainers.container.PrebidServerContainer +import org.prebid.server.functional.util.ObjectMapperWrapper +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +import java.time.Duration +import java.time.Instant +import java.time.ZoneId +import java.time.format.DateTimeFormatter + +import static io.restassured.RestAssured.given +import static io.restassured.parsing.Parser.JSON +import static java.time.ZoneOffset.UTC + +class PrebidServerService { + + static final String AUCTION_ENDPOINT = "/openrtb2/auction" + static final String AMP_ENDPOINT = "/openrtb2/amp" + static final String COOKIE_SYNC_ENDPOINT = "/cookie_sync" + static final String SET_UID_ENDPOINT = "/setuid" + static final String GET_UIDS_ENDPOINT = "/getuids" + static final String EVENT_ENDPOINT = "/event" + static final String VTRACK_ENDPOINT = "/vtrack" + static final String STATUS_ENDPOINT = "/status" + static final String INFO_BIDDERS_ENDPOINT = "/info/bidders" + static final String BIDDERS_PARAMS_ENDPOINT = "/bidders/params" + static final String CURRENCY_RATES_ENDPOINT = "/currency/rates" + static final String HTTP_INTERACTION_ENDPOINT = "/logging/httpinteraction" + static final String COLLECTED_METRICS_ENDPOINT = "/collected-metrics" + + private final PrebidServerContainer pbsContainer + private final ObjectMapperWrapper mapper + private final RequestSpecification requestSpecification + private final RequestSpecification adminRequestSpecification + + private final Logger log = LoggerFactory.getLogger(PrebidServerService.class) + + PrebidServerService(PrebidServerContainer pbsContainer, ObjectMapperWrapper mapper) { + def authenticationScheme = new BasicAuthScheme() + authenticationScheme.userName = pbsContainer.ADMIN_ENDPOINT_USERNAME + authenticationScheme.password = pbsContainer.ADMIN_ENDPOINT_PASSWORD + this.pbsContainer = pbsContainer + this.mapper = mapper + requestSpecification = new RequestSpecBuilder().setBaseUri(pbsContainer.rootUri) + .build() + adminRequestSpecification = new RequestSpecBuilder().setBaseUri(pbsContainer.adminRootUri) + .setAuth(authenticationScheme) + .build() + } + + @Step("[POST] /openrtb2/auction") + BidResponse sendAuctionRequest(BidRequest bidRequest) { + def payload = mapper.encode(bidRequest) + + def response = given(requestSpecification).body(payload) + .post(AUCTION_ENDPOINT) + + checkResponseStatusCode(response) + response.as(BidResponse) + } + + @Step("[POST] /openrtb2/auction") + BidResponse sendAuctionRequest(BidRequest bidRequest, Map headers) { + def payload = mapper.encode(bidRequest) + + def response = given(requestSpecification).headers(headers) + .body(payload) + .post(AUCTION_ENDPOINT) + + checkResponseStatusCode(response) + response.as(BidResponse) + } + + @Step("[GET] /openrtb2/amp") + AmpResponse sendAmpRequest(AmpRequest ampRequest) { + def response = given(requestSpecification).queryParams(mapper.toMap(ampRequest)) + .get(AMP_ENDPOINT) + + checkResponseStatusCode(response) + response.as(AmpResponse) + } + + @Step("[POST] /cookie_sync without cookie") + CookieSyncResponse sendCookieSyncRequest(CookieSyncRequest request) { + def payload = mapper.encode(request) + def response = given(requestSpecification).body(payload) + .post(COOKIE_SYNC_ENDPOINT) + + checkResponseStatusCode(response) + response.as(CookieSyncResponse) + } + + @Step("[POST] /cookie_sync with cookie") + CookieSyncResponse sendCookieSyncRequest(CookieSyncRequest request, UidsCookie uidsCookie) { + def uidsCookieAsJson = mapper.encode(uidsCookie) + def uidsCookieAsEncodedJson = Base64.urlEncoder.encodeToString(uidsCookieAsJson.bytes) + + def payload = mapper.encode(request) + def response = given(requestSpecification).cookie("uids", uidsCookieAsEncodedJson) + .body(payload) + .post(COOKIE_SYNC_ENDPOINT) + + checkResponseStatusCode(response) + response.as(CookieSyncResponse) + } + + @Step("[GET] /setuid") + SetuidResponse sendSetUidRequest(SetuidRequest request, UidsCookie uidsCookie) { + def uidsCookieAsJson = mapper.encode(uidsCookie) + def uidsCookieAsEncodedJson = Base64.urlEncoder.encodeToString(uidsCookieAsJson.bytes) + def response = given(requestSpecification).cookie("uids", uidsCookieAsEncodedJson) + .queryParams(mapper.toMap(request)) + .get(SET_UID_ENDPOINT) + + checkResponseStatusCode(response) + + def setuidResponse = new SetuidResponse() + setuidResponse.uidsCookie = response.detailedCookie("uids") + setuidResponse.responseBody = response.asString() + setuidResponse + } + + @Step("[GET] /getuids") + GetuidResponse sendGetUidRequest(UidsCookie uidsCookie) { + def uidsCookieAsJson = mapper.encode(uidsCookie) + def uidsCookieAsEncodedJson = Base64.urlEncoder.encodeToString(uidsCookieAsJson.bytes) + + def response = given(requestSpecification).cookie("uids", uidsCookieAsEncodedJson) + .get(GET_UIDS_ENDPOINT) + + checkResponseStatusCode(response) + response.as(GetuidResponse) + } + + @Step("[GET] /event") + byte[] sendEventRequest(EventRequest eventRequest) { + def response = given(requestSpecification).queryParams(mapper.toMap(eventRequest)) + .get(EVENT_ENDPOINT) + + checkResponseStatusCode(response) + response.body.asByteArray() + } + + @Step("[POST] /vtrack") + PrebidCacheResponse sendVtrackRequest(VtrackRequest request, String account) { + def response = given(requestSpecification).queryParam("a", account) + .body(request) + .post(VTRACK_ENDPOINT) + + checkResponseStatusCode(response) + response.as(PrebidCacheResponse) + } + + @Step("[GET] /status") + StatusResponse sendStatusRequest() { + def response = given(requestSpecification).get(STATUS_ENDPOINT) + + checkResponseStatusCode(response) + + response.as(StatusResponse) + } + + @Step("[GET] /info/bidders") + String sendInfoBiddersRequest(boolean enabledOnly = true) { + def response = given(requestSpecification).queryParam("enabledonly", enabledOnly) + .get(INFO_BIDDERS_ENDPOINT) + + checkResponseStatusCode(response) + response.body().asString() + } + + @Step("[GET] /info/bidders/{bidderName}") + BidderInfoResponse sendBidderInfoRequest(BidderName bidderName) { + + def response = given(requestSpecification).get("$INFO_BIDDERS_ENDPOINT/$bidderName.value") + + checkResponseStatusCode(response) + response.as(BidderInfoResponse) + } + + @Step("[GET] /bidders/params") + BiddersParamsResponse sendBiddersParamsRequest() { + RestAssured.defaultParser = JSON + def response = given(requestSpecification).get(BIDDERS_PARAMS_ENDPOINT) + + checkResponseStatusCode(response) + response.as(BiddersParamsResponse) + } + + @Step("[GET] /currency/rates") + CurrencyRatesResponse sendCurrencyRatesRequest() { + def response = given(adminRequestSpecification).get(CURRENCY_RATES_ENDPOINT) + + checkResponseStatusCode(response) + response.as(CurrencyRatesResponse) + } + + @Step("[GET] /logging/httpinteraction") + String sendLoggingHttpInteractionRequest(HttpInteractionRequest httpInteractionRequest) { + def response = given(adminRequestSpecification).queryParams(mapper.toMap(httpInteractionRequest)) + .get(HTTP_INTERACTION_ENDPOINT) + + checkResponseStatusCode(response) + response.body().asString() + } + + @Step("[GET] /collected-metrics") + Map sendCollectedMetricsRequest() { + def response = given(adminRequestSpecification).get(COLLECTED_METRICS_ENDPOINT) + + checkResponseStatusCode(response) + mapper.decode(response.asString(), new TypeReference>() {}) + } + + private void checkResponseStatusCode(Response response) { + def statusCode = response.statusCode + if (statusCode != 200) { + def responseBody = response.body.asString() + log.error(responseBody) + throw new PrebidServerException(statusCode, responseBody) + } + } + + List getLogsByTime(Instant testStart, + Instant testEnd = Instant.now()) { + if (!testEnd.isAfter(testStart)) { + throw new IllegalArgumentException("The end time of the test is less than the start time") + } + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") + .withZone(ZoneId.from(UTC)) + def logs = Arrays.asList(pbsContainer.logs.split("\n")) + def filteredLogs = [] + + def deltaTime = Duration.between(testStart, testEnd).seconds + + for (int i = 0; i <= deltaTime; i++) { + def time = testStart.plusSeconds(i) + def element = logs.find { it.contains(formatter.format(time)) } + if (element) { + filteredLogs.addAll(logs.subList(logs.indexOf(element), logs.size())) + break + } + } + filteredLogs + } +} diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/Dependencies.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/Dependencies.groovy new file mode 100644 index 00000000000..585a327bf52 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/Dependencies.groovy @@ -0,0 +1,36 @@ +package org.prebid.server.functional.testcontainers + +import org.prebid.server.functional.testcontainers.container.NetworkServiceContainer +import org.prebid.server.functional.util.ObjectMapperWrapper +import org.testcontainers.containers.MySQLContainer +import org.testcontainers.containers.Network +import org.testcontainers.lifecycle.Startables + +class Dependencies { + + static final ObjectMapperWrapper objectMapperWrapper = new ObjectMapperWrapper() + + static final Network network = Network.newNetwork() + + static final MySQLContainer mysqlContainer = new MySQLContainer<>("mysql:8.0.26") + .withDatabaseName("prebid") + .withUsername("prebid") + .withPassword("prebid") + .withInitScript("org/prebid/server/functional/db_schema.sql") + .withNetwork(network) + + static final NetworkServiceContainer networkServiceContainer = new NetworkServiceContainer(System.getProperty("mockserver.version")) + .withNetwork(network) + + static void start() { + Startables.deepStart([networkServiceContainer, mysqlContainer]) + .join() + } + + static void stop() { + [networkServiceContainer, mysqlContainer].parallelStream() + .forEach({ it.stop() }) + } + + private Dependencies() {} // should not be instantiated +} diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/ErrorListener.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/ErrorListener.groovy new file mode 100644 index 00000000000..5bab87bbd32 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/ErrorListener.groovy @@ -0,0 +1,51 @@ +package org.prebid.server.functional.testcontainers + +import io.qameta.allure.Attachment +import io.restassured.RestAssured +import io.restassured.filter.log.RequestLoggingFilter +import io.restassured.filter.log.ResponseLoggingFilter +import org.spockframework.runtime.AbstractRunListener +import org.spockframework.runtime.model.ErrorInfo +import org.spockframework.runtime.model.IterationInfo +import org.spockframework.runtime.model.SpecInfo + +class ErrorListener extends AbstractRunListener { + + ByteArrayOutputStream request = new ByteArrayOutputStream() + ByteArrayOutputStream response = new ByteArrayOutputStream() + PrintStream requestVar = new PrintStream(request, true) + PrintStream responseVar = new PrintStream(response, true) + + @Override + void beforeSpec(SpecInfo spec) { + RestAssured.filters(new RequestLoggingFilter(requestVar)) + RestAssured.filters(new ResponseLoggingFilter(responseVar)) + } + + @Override + void beforeIteration(IterationInfo iteration) { + request.reset() + } + + @Override + void error(ErrorInfo error) { + logRequest(request) + logResponse(response) + } + + @Attachment(value = "request") + private static byte[] logRequest(ByteArrayOutputStream stream) { + attach(stream) + } + + @Attachment(value = "response") + private static byte[] logResponse(ByteArrayOutputStream stream) { + attach(stream) + } + + private static byte[] attach(ByteArrayOutputStream log) { + byte[] array = log.toByteArray() + log.reset() + array + } +} diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/PBSTest.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/PBSTest.groovy new file mode 100644 index 00000000000..baca36e1d15 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/PBSTest.groovy @@ -0,0 +1,18 @@ +package org.prebid.server.functional.testcontainers + +import org.spockframework.runtime.extension.ExtensionAnnotation + +import java.lang.annotation.Documented +import java.lang.annotation.ElementType +import java.lang.annotation.Inherited +import java.lang.annotation.Retention +import java.lang.annotation.RetentionPolicy +import java.lang.annotation.Target + +@Documented +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@ExtensionAnnotation(PBSTestExtension.class) +@interface PBSTest { +} diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/PBSTestExtension.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/PBSTestExtension.groovy new file mode 100644 index 00000000000..c4ebeece83c --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/PBSTestExtension.groovy @@ -0,0 +1,12 @@ +package org.prebid.server.functional.testcontainers + +import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension +import org.spockframework.runtime.model.SpecInfo + +class PBSTestExtension extends AbstractAnnotationDrivenExtension { + + @Override + void visitSpecAnnotation(PBSTest annotation, SpecInfo spec) { + spec.addListener(new ErrorListener()) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/PbsServiceFactory.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/PbsServiceFactory.groovy new file mode 100644 index 00000000000..9aa2f62f142 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/PbsServiceFactory.groovy @@ -0,0 +1,64 @@ +package org.prebid.server.functional.testcontainers + +import org.prebid.server.functional.service.PrebidServerService +import org.prebid.server.functional.testcontainers.container.NetworkServiceContainer +import org.prebid.server.functional.testcontainers.container.PrebidServerContainer +import org.prebid.server.functional.util.ObjectMapperWrapper + +class PbsServiceFactory { + + private static final Map, PrebidServerContainer> containers = [:] + private static final int MAX_CONTAINERS_COUNT = 5 + + private final ObjectMapperWrapper mapper + private final NetworkServiceContainer networkServiceContainer + + PbsServiceFactory(NetworkServiceContainer networkServiceContainer, ObjectMapperWrapper mapper) { + this.networkServiceContainer = networkServiceContainer + this.mapper = mapper + } + + PrebidServerService getService(Map config) { + if (containers.size() >= MAX_CONTAINERS_COUNT) { + def container = containers.find { !it.key.isEmpty() } + remove([(container.key): container.value]) + } + if (containers.containsKey(config)) { + return new PrebidServerService(containers.get(config), mapper) + } else { + def pbsContainer = new PrebidServerContainer(config) + pbsContainer.start() + containers.put(config, pbsContainer) + return new PrebidServerService(pbsContainer, mapper) + } + } + + static void stopContainers() { + def containers = containers.findAll { it.key != [:] } + remove(containers) + } + + Map pubstackAnalyticsConfig(String scopeId) { + ["analytics.pubstack.enabled" : "true", + "analytics.pubstack.endpoint" : networkServiceContainer.rootUri, + "analytics.pubstack.scopeid" : scopeId, + "analytics.pubstack.configuration-refresh-delay-ms": "1000", + "analytics.pubstack.buffers.size-bytes" : "1", + "analytics.pubstack.timeout-ms" : "100"] + } + + Map httpSettings() { + ["settings.http.endpoint" : "$networkServiceContainer.rootUri/stored-requests".toString(), + "settings.http.amp-endpoint" : "$networkServiceContainer.rootUri/amp-stored-requests".toString(), + "settings.http.video-endpoint": "$networkServiceContainer.rootUri/video-stored-requests".toString()] + } + + private static void remove(Map, PrebidServerContainer> map) { + map.each { key, value -> + value.stop() + containers.remove(key) + } + } +} + + diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/TestcontainersExtension.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/TestcontainersExtension.groovy new file mode 100644 index 00000000000..285f42ed18b --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/TestcontainersExtension.groovy @@ -0,0 +1,16 @@ +package org.prebid.server.functional.testcontainers + +import org.spockframework.runtime.extension.AbstractGlobalExtension + +class TestcontainersExtension extends AbstractGlobalExtension { + + @Override + void start() { + Dependencies.start() + } + + @Override + void stop() { + Dependencies.stop() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/container/NetworkServiceContainer.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/container/NetworkServiceContainer.groovy new file mode 100644 index 00000000000..eadeb5bbbf6 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/container/NetworkServiceContainer.groovy @@ -0,0 +1,29 @@ +package org.prebid.server.functional.testcontainers.container + +import org.testcontainers.containers.MockServerContainer +import org.testcontainers.containers.Network + +class NetworkServiceContainer extends MockServerContainer { + + NetworkServiceContainer(String version) { + super(version) + } + + String getHostAndPort() { + "${networkAliases.first()}:${exposedPorts.first()}" + } + + String getRootUri() { + "http://${getHostAndPort()}" + } + + @Override + NetworkServiceContainer withNetwork(Network network) { + return super.withNetwork(network) as NetworkServiceContainer + } + + @Override + void close() { + super.close() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/container/PrebidServerContainer.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/container/PrebidServerContainer.groovy new file mode 100644 index 00000000000..ee13e094c6d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/container/PrebidServerContainer.groovy @@ -0,0 +1,167 @@ +package org.prebid.server.functional.testcontainers.container + +import org.prebid.server.functional.testcontainers.Dependencies +import org.testcontainers.containers.GenericContainer +import org.testcontainers.containers.MySQLContainer +import org.testcontainers.containers.wait.strategy.Wait + +class PrebidServerContainer extends GenericContainer { + + public static final int PORT = 8080 + public static final int DEBUG_PORT = 8000 + public static final int ADMIN_PORT = 8060 + public static final String ADMIN_ENDPOINT_USERNAME = "user" + public static final String ADMIN_ENDPOINT_PASSWORD = "user" + + private static final String DB_ACCOUNT_QUERY = """ +SELECT JSON_MERGE_PATCH(JSON_OBJECT('id', uuid, + 'status', status, + 'auction', JSON_OBJECT('price-granularity', price_granularity, + 'banner-cache-ttl', banner_cache_ttl, + 'video-cache-ttl', video_cache_ttl, + 'truncate-target-attr', truncate_target_attr, + 'default-integration', default_integration, + 'bid-validations', bid_validations, + 'events', JSON_OBJECT('enabled', NOT NOT (events_enabled))), + 'privacy', JSON_OBJECT('gdpr', tcf_config), + 'analytics', analytics_config), + COALESCE(config, '{}')) as consolidated_config +FROM accounts_account +WHERE uuid = %ACCOUNT_ID% +LIMIT 1 +""" + + private static final Map DEFAULT_ENV = [ + "auction.ad-server-currency" : "USD", + "auction.stored-requests-timeout-ms" : "1000", + "metrics.prefix" : "prebid", + "status-response" : "ok", + "gdpr.default-value" : "0", + "settings.database.account-query" : DB_ACCOUNT_QUERY, + "settings.database.stored-requests-query" : "SELECT accountId, reqid, requestData, 'request' as dataType FROM stored_requests WHERE reqid IN (%REQUEST_ID_LIST%) UNION ALL SELECT accountId, reqid, requestData, 'imp' as dataType FROM stored_requests WHERE reqid IN (%IMP_ID_LIST%)", + "settings.database.amp-stored-requests-query": "SELECT accountId, reqid, requestData, 'request' as dataType FROM stored_requests WHERE reqid IN (%REQUEST_ID_LIST%)", + "settings.database.stored-responses-query" : "SELECT resid, responseData FROM stored_responses WHERE resid IN (%RESPONSE_ID_LIST%)" + ] + + PrebidServerContainer(Map config) { + this("prebid/prebid-server:latest", config) + } + + PrebidServerContainer(String dockerImage, Map config) { + super(dockerImage) + withExposedPorts(PORT, DEBUG_PORT, ADMIN_PORT) + waitingFor(Wait.forHttp("/status") + .forPort(PORT) + .forStatusCode(200)) + withConfig(DEFAULT_ENV) + withAdminEndpoints() + withDebug() + withMysql(Dependencies.mysqlContainer) + withBidder(Dependencies.networkServiceContainer) + withDefaultBiddersConfig() + withPrebidCache(Dependencies.networkServiceContainer) + withMetricsEndpoint() + withNetwork(Dependencies.network) + withConfig(config) + } + + PrebidServerContainer withMysql(MySQLContainer mysql) { + withMysql(mysql.getNetworkAliases().get(0), + mysql.exposedPorts.get(0), + mysql.databaseName, + mysql.username, + mysql.password) + return self() + } + + PrebidServerContainer withDebug() { + withEnv("JAVA_TOOL_OPTIONS", "-agentlib:jdwp=transport=dt_socket,address=$DEBUG_PORT,server=y,suspend=n") + } + + void withMysql(String host, int port, String dbname, String user, String password) { + withConfig(["settings.database.type" : "mysql", + "settings.database.host" : host, + "settings.database.port" : port as String, + "settings.database.dbname" : dbname, + "settings.database.user" : user, + "settings.database.password" : password, + "settings.database.pool-size": "2" // setting 2 here to leave some slack for the PBS + ]) + } + + PrebidServerContainer withAdminEndpoints() { + withConfig(["admin-endpoints.currency-rates.enabled" : "true", + "currency-converter.external-rates.enabled" : "true", + ("admin-endpoints.credentials.$ADMIN_ENDPOINT_USERNAME".toString()): ADMIN_ENDPOINT_PASSWORD, + "admin-endpoints.logging-httpinteraction.enabled" : "true" + ]) + return self() + } + + PrebidServerContainer withBidder(NetworkServiceContainer networkServiceContainer) { + withBidder("$networkServiceContainer.rootUri") + return self() + } + + PrebidServerContainer withPrebidCache(NetworkServiceContainer networkServiceContainer) { + withConfig(["cache.scheme": "http", + "cache.host" : "$networkServiceContainer.hostAndPort".toString(), + "cache.path" : "/cache", + "cache.query" : "uuid=" + ]) + return self() + } + + void withBidder(String host) { + withConfig(["adapters.generic.enabled" : "true", + "adapters.generic.endpoint" : "$host/auction".toString(), + "adapters.generic.usersync.url" : "$host/generic-usersync".toString(), + "adapters.generic.usersync.type": "redirect" + ]) + } + + void withDefaultBiddersConfig() { + withConfig(["adapter-defaults.enabled" : "false", + "adapter-defaults.modifying-vast-xml-allowed": "true", + "adapter-defaults.pbs-enforces-ccpa" : "true" + ]) + } + + void withMetricsEndpoint() { + withConfig(["admin-endpoints.collected-metrics.enabled": "true"]) + } + + PrebidServerContainer withConfig(Map config) { + withEnv(normalizeProperties(config)) + } + + int getPort() { + getMappedPort(PORT) + } + + int getAdminPort() { + getMappedPort(ADMIN_PORT) + } + + String getRootUri() { + return "http://$host:$port" + } + + String getAdminRootUri() { + return "http://$host:$adminPort" + } + + private static Map normalizeProperties(Map properties) { + properties.collectEntries { [normalizeProperty(it.key), it.value] } as Map + } + + private static String normalizeProperty(String property) { + property.replace(".", "_") + .replace("-", "") + } + + @Override + void close() { + super.close() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/Bidder.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/Bidder.groovy new file mode 100644 index 00000000000..df9be7d5e75 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/Bidder.groovy @@ -0,0 +1,68 @@ +package org.prebid.server.functional.testcontainers.scaffolding + +import org.mockserver.matchers.TimeToLive +import org.mockserver.matchers.Times +import org.mockserver.model.HttpRequest +import org.mockserver.model.HttpResponse +import org.prebid.server.functional.model.bidderspecific.BidderRequest +import org.prebid.server.functional.model.response.auction.BidResponse +import org.prebid.server.functional.util.ObjectMapperWrapper +import org.testcontainers.containers.MockServerContainer + +import static org.mockserver.model.HttpRequest.request +import static org.mockserver.model.HttpResponse.response +import static org.mockserver.model.HttpStatusCode.OK_200 +import static org.mockserver.model.JsonPathBody.jsonPath + +class Bidder extends NetworkScaffolding { + + private static final String AUCTION_ENDPOINT = "/auction" + + Bidder(MockServerContainer mockServerContainer, ObjectMapperWrapper mapper) { + super(mockServerContainer, AUCTION_ENDPOINT, mapper) + } + + @Override + protected HttpRequest getRequest(String bidRequestId) { + request().withPath(AUCTION_ENDPOINT) + .withBody(jsonPath("\$[?(@.id == '$bidRequestId')]")) + } + + @Override + protected HttpRequest getRequest() { + request().withPath(AUCTION_ENDPOINT) + } + + @Override + void setResponse() { + mockServerClient.when(request().withPath(endpoint), Times.unlimited(), TimeToLive.unlimited(), -10) + .respond{request -> request.withPath(endpoint) + ? response().withStatusCode(OK_200.code()).withBody(getBodyByRequest(request)) + : HttpResponse.notFoundResponse()} + } + + List getBidderRequests(String bidRequestId) { + getRecordedRequestsBody(bidRequestId).collect { mapper.decode(it, BidderRequest) } + } + + BidderRequest getBidderRequest(String bidRequestId) { + def bidderRequests = getBidderRequests(bidRequestId) + def bidderCallCount = bidderRequests.size() + + if (bidderCallCount != 1) { + throw new IllegalStateException("Expecting exactly 1 bidder call. Got $bidderCallCount") + } + + bidderRequests.first() + } + + private String getBodyByRequest(HttpRequest request) { + def requestString = request.bodyAsString + def jsonNode = mapper.toJsonNode(requestString) + def id = jsonNode.get("id").asText() + def imp = jsonNode.get("imp") + def impIds = imp.collect { it.get("id").asText() } as List + def response = BidResponse.getDefaultBidResponse(id, impIds) + mapper.encode(response) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/HttpSettings.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/HttpSettings.groovy new file mode 100644 index 00000000000..304a44ed845 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/HttpSettings.groovy @@ -0,0 +1,33 @@ +package org.prebid.server.functional.testcontainers.scaffolding + +import org.mockserver.model.HttpRequest +import org.prebid.server.functional.util.ObjectMapperWrapper +import org.testcontainers.containers.MockServerContainer + +import static org.mockserver.model.HttpRequest.request + +class HttpSettings extends NetworkScaffolding { + + private static final String ENDPOINT = "/stored-requests" + private static final String AMP_ENDPOINT = "/amp-stored-requests" + + HttpSettings(MockServerContainer mockServerContainer, ObjectMapperWrapper mapper) { + super(mockServerContainer, ENDPOINT, mapper) + } + + @Override + protected HttpRequest getRequest(String accountId) { + request().withPath(ENDPOINT) + .withQueryStringParameter("account-ids", "[\"$accountId\"]") + } + + @Override + protected HttpRequest getRequest() { + request().withPath(ENDPOINT) + } + + @Override + void setResponse() { + + } +} diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/NetworkScaffolding.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/NetworkScaffolding.groovy new file mode 100644 index 00000000000..a3cf442c491 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/NetworkScaffolding.groovy @@ -0,0 +1,126 @@ +package org.prebid.server.functional.testcontainers.scaffolding + +import org.mockserver.client.MockServerClient +import org.mockserver.matchers.Times +import org.mockserver.model.HttpRequest +import org.prebid.server.functional.model.ResponseModel +import org.prebid.server.functional.util.ObjectMapperWrapper +import org.testcontainers.containers.MockServerContainer + +import static java.util.concurrent.TimeUnit.SECONDS +import static org.mockserver.model.HttpRequest.request +import static org.mockserver.model.HttpResponse.response +import static org.mockserver.model.HttpStatusCode.OK_200 +import static org.mockserver.model.MediaType.APPLICATION_JSON + +abstract class NetworkScaffolding { + + protected MockServerClient mockServerClient + protected String endpoint + protected ObjectMapperWrapper mapper + + NetworkScaffolding(MockServerContainer mockServerContainer, String endpoint, ObjectMapperWrapper mapper) { + this.mockServerClient = new MockServerClient(mockServerContainer.host, mockServerContainer.serverPort) + this.endpoint = endpoint + this.mapper = mapper + } + + abstract protected HttpRequest getRequest(String value) + + abstract protected HttpRequest getRequest() + + abstract void setResponse() + + int getRequestCount(HttpRequest httpRequest) { + mockServerClient.retrieveRecordedRequests(httpRequest) + .size() + } + + int getRequestCount(String value) { + mockServerClient.retrieveRecordedRequests(getRequest(value)) + .size() + } + + int getRequestCount() { + mockServerClient.retrieveRecordedRequests(request) + .size() + } + + void setResponse(HttpRequest httpRequest, ResponseModel responseModel) { + def mockResponse = mapper.encode(responseModel) + mockServerClient.when(httpRequest, Times.exactly(1)) + .respond(response().withStatusCode(OK_200.code()) + .withBody(mockResponse, APPLICATION_JSON)) + } + + void setResponse(String value, ResponseModel responseModel) { + def mockResponse = mapper.encode(responseModel) + mockServerClient.when(getRequest(value), Times.exactly(1)) + .respond(response().withStatusCode(OK_200.code()) + .withBody(mockResponse, APPLICATION_JSON)) + } + + void setResponse(ResponseModel responseModel) { + def mockResponse = mapper.encode(responseModel) + mockServerClient.when(request().withPath(endpoint)) + .respond(response().withStatusCode(OK_200.code()) + .withBody(mockResponse, APPLICATION_JSON)) + } + + void setResponse(String value, int httpStatusCode) { + mockServerClient.when(getRequest(value), Times.exactly(1)) + .respond(response().withStatusCode(httpStatusCode)) + } + + void setResponse(String value, int httpStatusCode, String errorText) { + mockServerClient.when(getRequest(value), Times.exactly(1)) + .respond(response().withStatusCode(httpStatusCode) + .withBody(errorText, APPLICATION_JSON)) + } + + void setResponseWithTimeout(String value) { + mockServerClient.when(getRequest(value), Times.exactly(1)) + .respond(response().withDelay(SECONDS, 5)) + } + + List getRecordedRequestsBody(HttpRequest httpRequest) { + mockServerClient.retrieveRecordedRequests(httpRequest) + .collect { it.body.toString() } + } + + List getRecordedRequestsBody(String value) { + mockServerClient.retrieveRecordedRequests(getRequest(value)) + .collect { it.body.toString() } + } + + List getRecordedRequestsBody() { + mockServerClient.retrieveRecordedRequests(request) + .collect { it.body.toString() } + } + + Map getLastRecordedRequestHeaders(HttpRequest httpRequest) { + getRecordedRequestsHeaders(httpRequest).last() + } + + List> getRecordedRequestsHeaders(HttpRequest httpRequest) { + getRequestsHeaders(mockServerClient.retrieveRecordedRequests(httpRequest) as List) + } + + Map getLastRecordedRequestHeaders(String value) { + getRecordedRequestsHeaders(value).last() + } + + List> getRecordedRequestsHeaders(String value) { + getRequestsHeaders(mockServerClient.retrieveRecordedRequests(getRequest(value)) as List) + } + + void reset() { + mockServerClient.clear(request().withPath(endpoint)) + } + + private static List> getRequestsHeaders(List httpRequests) { + httpRequests*.headers*.entries*.collectEntries { header -> + [header.name as String, header.values.collect { it as String }] + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/PrebidCache.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/PrebidCache.groovy new file mode 100644 index 00000000000..252e499cbbb --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/PrebidCache.groovy @@ -0,0 +1,67 @@ +package org.prebid.server.functional.testcontainers.scaffolding + +import org.mockserver.matchers.TimeToLive +import org.mockserver.matchers.Times +import org.mockserver.model.HttpRequest +import org.mockserver.model.HttpResponse +import org.prebid.server.functional.model.mock.services.prebidcache.response.PrebidCacheResponse +import org.prebid.server.functional.util.ObjectMapperWrapper +import org.testcontainers.containers.MockServerContainer + +import static org.mockserver.model.HttpRequest.request +import static org.mockserver.model.HttpResponse.response +import static org.mockserver.model.HttpStatusCode.OK_200 +import static org.mockserver.model.JsonPathBody.jsonPath + +class PrebidCache extends NetworkScaffolding { + + private static final String CACHE_ENDPOINT = "/cache" + + PrebidCache(MockServerContainer mockServerContainer, ObjectMapperWrapper mapper) { + super(mockServerContainer, CACHE_ENDPOINT, mapper) + } + + void setXmlCacheResponse(String payload, PrebidCacheResponse prebidCacheResponse) { + setResponse(getXmlCacheRequest(payload), prebidCacheResponse) + } + + int getXmlRequestCount(String payload) { + getRequestCount(getXmlCacheRequest(payload)) + } + + List getXmlRecordedRequestsBody(String payload) { + getRecordedRequestsBody(getXmlCacheRequest(payload)) + } + + Map getXmlRecordedRequestHeaders(String payload) { + getLastRecordedRequestHeaders(getXmlCacheRequest(payload)) + } + + @Override + protected HttpRequest getRequest(String impId) { + request().withMethod("POST") + .withPath(CACHE_ENDPOINT) + .withBody(jsonPath("\$.puts[?(@.value.impid == '$impId')]")) + } + + @Override + HttpRequest getRequest() { + request().withMethod("POST") + .withPath(CACHE_ENDPOINT) + } + + @Override + void setResponse() { + def json = mapper.encode(PrebidCacheResponse.defaultCacheResponse) + mockServerClient.when(request().withPath(endpoint), Times.unlimited(), TimeToLive.unlimited(), -10) + .respond{request -> request.withPath(endpoint) + ? response().withStatusCode(OK_200.code()).withBody(json) + : HttpResponse.notFoundResponse()} + } + + private static HttpRequest getXmlCacheRequest(String payload) { + request().withMethod("POST") + .withPath(CACHE_ENDPOINT) + .withBody(jsonPath("\$.puts[?(@.value =~/^.*$payload.*\$/)]")) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/PubStackAnalytics.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/PubStackAnalytics.groovy new file mode 100644 index 00000000000..9d6480729f8 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/scaffolding/PubStackAnalytics.groovy @@ -0,0 +1,32 @@ +package org.prebid.server.functional.testcontainers.scaffolding + +import org.mockserver.model.HttpRequest +import org.prebid.server.functional.util.ObjectMapperWrapper +import org.testcontainers.containers.MockServerContainer + +import static org.mockserver.model.HttpRequest.request + +class PubStackAnalytics extends NetworkScaffolding { + + private static final String CONFIG_ENDPOINT = "/bootstrap" + private static final String ANALYTICS_ENDPOINT = "/intake/auction" + + PubStackAnalytics(MockServerContainer mockServerContainer, ObjectMapperWrapper mapper) { + super(mockServerContainer, CONFIG_ENDPOINT, mapper) + } + + @Override + protected HttpRequest getRequest() { + request().withPath(ANALYTICS_ENDPOINT) + } + + @Override + void setResponse() { + + } + + @Override + protected HttpRequest getRequest(String value) { + request().withPath(ANALYTICS_ENDPOINT) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/util/ObjectMapperWrapper.groovy b/src/test/groovy/org/prebid/server/functional/util/ObjectMapperWrapper.groovy new file mode 100644 index 00000000000..ca845de713a --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/util/ObjectMapperWrapper.groovy @@ -0,0 +1,48 @@ +package org.prebid.server.functional.util + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.xml.XmlMapper +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule + +class ObjectMapperWrapper { + + private final ObjectMapper mapper + private final XmlMapper xmlMapper + + ObjectMapperWrapper() { + mapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL) + .registerModule(new JavaTimeModule()) + xmlMapper = new XmlMapper() + } + + String encode(Object object) { + mapper.writeValueAsString(object) + } + + def T decode(JsonNode jsonNode, Class clazz) { + mapper.treeToValue(jsonNode, clazz) + } + + def T decode(String jsonString, Class clazz) { + mapper.readValue(jsonString, clazz) + } + + def T decode(String jsonString, TypeReference typeReference) { + mapper.readValue(jsonString, typeReference) + } + + Map toMap(Object object) { + mapper.convertValue(object, Map.class) + } + + JsonNode toJsonNode(String jsonString) { + mapper.readTree(jsonString) + } + + String encodeXml(Object object) { + xmlMapper.writeValueAsString(object) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy b/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy new file mode 100644 index 00000000000..4020a66e203 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy @@ -0,0 +1,35 @@ +package org.prebid.server.functional.util + +import org.testcontainers.shaded.org.apache.commons.lang.RandomStringUtils + +import java.text.DecimalFormat +import java.util.stream.IntStream + +import static java.lang.Integer.MAX_VALUE +import static java.lang.Integer.MIN_VALUE + +class PBSUtils { + + static int getRandomNumber(int min = 0, int max = MAX_VALUE) { + new Random().nextInt(max - min) + min + } + + static int getRandomNegativeNumber(int min = MIN_VALUE + 1, int max = 0) { + getRandomNumber(min, max) + } + + static float getFractionalRandomNumber(int min = 0, int max = MAX_VALUE) { + new Random().nextFloat() * (max - min) + min + } + + static float getRoundFractionalNumber(float number, int numberDecimalPlaces) { + def stringBuilder = new StringBuilder().append("##.") + IntStream.range(0, numberDecimalPlaces).forEach { index -> stringBuilder.append("#") } + def format = new DecimalFormat(stringBuilder.toString()) + Float.valueOf(format.format(number)) + } + + static String getRandomString(int stringLength = 20) { + RandomStringUtils.randomAlphanumeric(stringLength) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/util/SystemProperties.groovy b/src/test/groovy/org/prebid/server/functional/util/SystemProperties.groovy new file mode 100644 index 00000000000..203314830bb --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/util/SystemProperties.groovy @@ -0,0 +1,14 @@ +package org.prebid.server.functional.util + +class SystemProperties { + + public static final PBS_HOST = System.getProperty("pbs.host") + public static final PBS_PORT = System.getProperty("pbs.port") + public static final PBS_ADMIN_PORT = System.getProperty("pbs.admin.port") + public static final BIDDER_HOST = System.getProperty("bidder.host") + public static final BIDDER_PORT = System.getProperty("bidder.port") + + private SystemProperties() { + throw new InstantiationException("This class should not be instantiated!") + } +} diff --git a/src/test/resources/META-INF/services/org.spockframework.runtime.extension.IGlobalExtension b/src/test/resources/META-INF/services/org.spockframework.runtime.extension.IGlobalExtension new file mode 100644 index 00000000000..e86265025dd --- /dev/null +++ b/src/test/resources/META-INF/services/org.spockframework.runtime.extension.IGlobalExtension @@ -0,0 +1 @@ +org.prebid.server.functional.testcontainers.TestcontainersExtension diff --git a/src/test/resources/org/prebid/server/functional/db_schema.sql b/src/test/resources/org/prebid/server/functional/db_schema.sql new file mode 100644 index 00000000000..47dc48e509e --- /dev/null +++ b/src/test/resources/org/prebid/server/functional/db_schema.sql @@ -0,0 +1,47 @@ +CREATE TABLE accounts_account +( + id SERIAL PRIMARY KEY, + uuid varchar(40) NOT NULL, + price_granularity varchar(6), + banner_cache_ttl INT, + video_cache_ttl INT, + events_enabled BIT, + tcf_config json, + truncate_target_attr TINYINT UNSIGNED, + default_integration varchar(64), + analytics_config varchar(512), + bid_validations json, + status enum ('active','inactive'), + config json, + updated_by int(11), + updated_by_user varchar(64), + updated timestamp +); +CREATE TABLE s2sconfig_config +( + id SERIAL PRIMARY KEY, + uuid varchar(40) NOT NULL, + config varchar(512) +); + + +CREATE TABLE stored_requests +( + id SERIAL PRIMARY KEY, + accountId varchar(40), + reqid varchar(40), + requestData json +); + +CREATE TABLE stored_imps +( + id SERIAL PRIMARY KEY, + uuid varchar(40) NOT NULL, + config varchar(1024) +); +CREATE TABLE stored_responses +( + id SERIAL PRIMARY KEY, + uuid varchar(40) NOT NULL, + config varchar(1024) +); diff --git a/src/test/resources/org/prebid/server/functional/tracking-pixel.png b/src/test/resources/org/prebid/server/functional/tracking-pixel.png new file mode 100644 index 00000000000..1f0f2508535 Binary files /dev/null and b/src/test/resources/org/prebid/server/functional/tracking-pixel.png differ