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 index 97461b79d78..ce75803a3bb 100644 --- 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 @@ -18,7 +18,8 @@ class Banner { Integer pos List mimes Integer topframe - List expdir + @JsonProperty("expdir") + List expandableDirections List api String id Integer vcm diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/ConsentedProvidersSettings.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/ConsentedProvidersSettings.groovy index aa7bd511cb2..778bb27a277 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/ConsentedProvidersSettings.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/ConsentedProvidersSettings.groovy @@ -2,8 +2,10 @@ package org.prebid.server.functional.model.request.auction import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.EqualsAndHashCode import groovy.transform.ToString +@EqualsAndHashCode @ToString(includeNames = true, ignoreNulls = true) @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy) class ConsentedProvidersSettings { 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 index 60ef8fa7884..44514ccba13 100644 --- 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 @@ -1,8 +1,10 @@ package org.prebid.server.functional.model.request.auction +import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import org.prebid.server.functional.util.PBSUtils +@EqualsAndHashCode @ToString(includeNames = true, ignoreNulls = true) class Device { diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/DeviceExt.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/DeviceExt.groovy index 72337563b4b..efd8da65aa5 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/DeviceExt.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/DeviceExt.groovy @@ -1,13 +1,18 @@ package org.prebid.server.functional.model.request.auction +import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonValue +import groovy.transform.EqualsAndHashCode import groovy.transform.ToString +@EqualsAndHashCode @ToString class DeviceExt { Atts atts String cdep + @JsonProperty("ifa_type") + String ifaType enum Atts { 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 index 13c97a36ba4..93a29c52e1b 100644 --- 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 @@ -110,4 +110,10 @@ class Imp { (audio ? AUDIO : null) ].findAll { it } } + + @JsonIgnore + Object getSingleMediaTypeData() { + return banner ?: video ?: nativeObj ?: audio + } + } 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 index 23b4e7f87a5..90ee496b3df 100644 --- 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 @@ -49,6 +49,8 @@ class Prebid { List profileNames @JsonProperty("kvps") Map keyValuePairs + String outputFormat + String outputModule static class Channel { diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/PublicCountryIp.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/PublicCountryIp.groovy index 9bdb94f007d..69194afd4b4 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/PublicCountryIp.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/PublicCountryIp.groovy @@ -1,5 +1,7 @@ package org.prebid.server.functional.model.request.auction +import inet.ipaddr.IPAddressString + enum PublicCountryIp { USA_IP("209.232.44.21", "d646:2414:17b2:f371:9b62:f176:b4c0:51cd"), @@ -14,4 +16,8 @@ enum PublicCountryIp { this.v4 = v4 this.v6 = ipV6 } + + String getMaskedIPv6() { + new IPAddressString(this.v6).toAddress().mask(new IPAddressString("::/64").toAddress().getNetworkMask()).toCanonicalString() + } } 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 index 6e647c16817..8ffc689be32 100644 --- 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 @@ -2,14 +2,17 @@ package org.prebid.server.functional.model.request.auction import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.EqualsAndHashCode import groovy.transform.ToString +@EqualsAndHashCode @ToString(includeNames = true, ignoreNulls = true) @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy) class Regs { Integer coppa Integer gdpr + Integer gpc String usPrivacy String gpp List gppSid 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 index d7e9cb2242e..fe4d4c851e4 100644 --- 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 @@ -2,8 +2,10 @@ package org.prebid.server.functional.model.request.auction import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.EqualsAndHashCode import groovy.transform.ToString +@EqualsAndHashCode @ToString(includeNames = true, ignoreNulls = true) @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy) class RegsExt { diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/StoredAuctionResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/StoredAuctionResponse.groovy index cde5f2268de..d6a3a98dd82 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/StoredAuctionResponse.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/StoredAuctionResponse.groovy @@ -1,9 +1,11 @@ package org.prebid.server.functional.model.request.auction import com.fasterxml.jackson.annotation.JsonProperty +import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import org.prebid.server.functional.model.response.auction.SeatBid +@EqualsAndHashCode @ToString(includeNames = true, ignoreNulls = true) class StoredAuctionResponse { diff --git a/src/test/groovy/org/prebid/server/functional/model/request/get/CommaSeparatedListSerializer.groovy b/src/test/groovy/org/prebid/server/functional/model/request/get/CommaSeparatedListSerializer.groovy new file mode 100644 index 00000000000..72189391ec5 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/get/CommaSeparatedListSerializer.groovy @@ -0,0 +1,14 @@ +package org.prebid.server.functional.model.request.get + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.databind.JsonSerializer +import com.fasterxml.jackson.databind.SerializerProvider + +class CommaSeparatedListSerializer extends JsonSerializer> { + + @Override + void serialize(List value, JsonGenerator generator, SerializerProvider serializers) throws IOException { + String result = value.join(',') + generator.writeString(result) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/get/GeneralGetRequest.groovy b/src/test/groovy/org/prebid/server/functional/model/request/get/GeneralGetRequest.groovy new file mode 100644 index 00000000000..e0395b091cd --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/get/GeneralGetRequest.groovy @@ -0,0 +1,298 @@ +package org.prebid.server.functional.model.request.get + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import org.prebid.server.functional.model.request.amp.ConsentType +import org.prebid.server.functional.model.request.auction.DebugCondition +import org.prebid.server.functional.model.request.auction.DeviceType +import org.prebid.server.functional.model.request.auction.VideoPlacementSubtypes +import org.prebid.server.functional.model.request.auction.VideoPlcmtSubtype +import org.prebid.server.functional.util.PBSUtils + +import static org.prebid.server.functional.model.request.auction.DebugCondition.ENABLED + +class GeneralGetRequest { + + @JsonProperty("srid") + String storedRequestId + + @JsonProperty("tag_id") + String storedRequestIdLegacy + + @JsonProperty("pubid") + String accountId + + @JsonProperty("account") + String accountIdLegacy + + @JsonProperty("tmax") + Integer timeoutMax + + DebugCondition debug + + @JsonProperty("of") + String outputFormat + + @JsonProperty("om") + String outputModule + + @JsonProperty("rprof") + @JsonSerialize(using = CommaSeparatedListSerializer) + List requestProfiles + + @JsonProperty("iprof") + @JsonSerialize(using = CommaSeparatedListSerializer) + List impProfiles + + @JsonProperty("sarid") + String storedAuctionResponseId + + List mimes + + @JsonProperty("w") + Integer width + + @JsonProperty("h") + Integer height + + @JsonProperty("ow") + Integer overrideWidth + + @JsonProperty("oh") + Integer overrideHeight + + Object sizes + + @JsonProperty("ms") + Object sizesLegacy + + String slot + + @JsonProperty("mindur") + Integer minDuration + + @JsonProperty("maxdur") + Integer maxDuration + + @JsonSerialize(using = CommaSeparatedListSerializer) + List api + + @JsonProperty("battr") + @JsonSerialize(using = CommaSeparatedListSerializer) + List blockAttributes + + @JsonSerialize(using = CommaSeparatedListSerializer) + List delivery + + Integer linearity + + @JsonProperty("minbr") + Integer minBitrate + + @JsonProperty("maxbr") + Integer maxBitrate + + @JsonProperty("maxex") + Integer maxExtended + + @JsonProperty("maxseq") + Integer maxSequence + + @JsonProperty("mincpms") + Integer minCpmPerSec + + @JsonProperty("poddur") + Integer podDuration + + @JsonProperty("podid") + Integer podId + + @JsonProperty("podseq") + Integer podSequence + + @JsonProperty("proto") + @JsonSerialize(using = CommaSeparatedListSerializer) + List protocols + + @JsonProperty("rqddurs") + @JsonSerialize(using = CommaSeparatedListSerializer) + List requiredDurations + + @JsonProperty("seq") + Integer sequence + + @JsonProperty("slotinpod") + Integer slotInPod + + @JsonProperty("startdelay") + Integer startDelay + + Integer skip + + @JsonProperty("skipafter") + Integer skipAfter + + @JsonProperty("skipmin") + Integer skipMin + + @JsonProperty("pos") + Integer position + + @JsonProperty("stitched") + Integer stitched + + Integer feed + + @JsonProperty("nvol") + Integer normalizedVolume + + VideoPlacementSubtypes placement + + @JsonProperty("plcmt") + VideoPlcmtSubtype placementSubtype + + @JsonProperty("playbackend") + Integer playbackEnd + + @JsonProperty("playbackmethod") + @JsonSerialize(using = CommaSeparatedListSerializer) + List playbackMethods + + @JsonProperty("boxingallowed") + Integer boxingAllowed + + @JsonProperty("btype") + @JsonSerialize(using = CommaSeparatedListSerializer) + List bannerTypes + + @JsonProperty("expdir") + @JsonSerialize(using = CommaSeparatedListSerializer) + List expandableDirections + + @JsonProperty("topframe") + Integer topFrame + + String targeting + + @JsonProperty("tcfc") + String tcfConsent + + @JsonProperty("gdpr_consent") + String generalConsent + + @JsonProperty("consent_string") + String generalConsentString + + Integer gdpr + + @JsonProperty("gdpr_applies") + Boolean gdprApplies + + @JsonProperty("addtl_consent") + String additionalConsent + + @JsonProperty("consent_type") + ConsentType consentType + + @JsonProperty("gppc") + String gpp + + @JsonProperty("gpps") + @JsonSerialize(using = CommaSeparatedListSerializer) + List gppSid + + @JsonProperty("gpc") + Integer globalPrivacyControl + + Integer coppa + + @JsonProperty("dnt") + Integer doNotTrack + + @JsonProperty("lmt") + Integer limitAdTracking + + @JsonProperty("usp") + String usPrivacy + + @JsonProperty("bcat") + @JsonSerialize(using = CommaSeparatedListSerializer) + List blockedCategories + + @JsonProperty("badv") + @JsonSerialize(using = CommaSeparatedListSerializer) + List blockedAdvertisers + + String page + + @JsonProperty("bundle") + String appBundle + + @JsonProperty("name") + String appName + + @JsonProperty("storeurl") + String storeUrl + + @JsonProperty("cgenre") + String contentGenre + + @JsonProperty("clang") + String contentLanguage + + @JsonProperty("crating") + String contentRating + + @JsonProperty("ccat") + Integer contentCategory + + @JsonProperty("ccattax") + Integer contentCategoryTaxonomy + + @JsonProperty("cseries") + String contentSeries + + @JsonProperty("rss_feed") + String contentSeriesAlias + + @JsonProperty("ctitle") + String contentTitle + + @JsonProperty("curl") + String contentUrl + + @JsonProperty("clivestream") + Integer contentLivestream + + @JsonProperty("ip") + String deviceIp + + @JsonProperty("ua") + String deviceUa + + @JsonProperty("dtype") + DeviceType deviceType + + @JsonProperty("ifa") + String deviceIfa + + @JsonProperty("ifat") + String deviceIfaType + + String unknown + + @JsonProperty("unknown_alias") + String unknownAlias + + static GeneralGetRequest getDefault(String storedRequestId = PBSUtils.randomNumber) { + new GeneralGetRequest(storedRequestId: storedRequestId, debug: ENABLED) + } + + String resolveStoredRequestId() { + storedRequestId ?: storedRequestIdLegacy + } + + String resolveAccountId() { + accountId ?: accountIdLegacy + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/get/GeneralGetResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/response/get/GeneralGetResponse.groovy new file mode 100644 index 00000000000..ec9e938bbda --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/get/GeneralGetResponse.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.response.get + +import groovy.transform.ToString +import org.prebid.server.functional.model.response.amp.AmpResponseExt + +@ToString(includeNames = true, ignoreNulls = true) +class GeneralGetResponse { + + Map targeting + GeneralGetResponseExt ext +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/get/GeneralGetResponseExt.groovy b/src/test/groovy/org/prebid/server/functional/model/response/get/GeneralGetResponseExt.groovy new file mode 100644 index 00000000000..619a6c4daf6 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/get/GeneralGetResponseExt.groovy @@ -0,0 +1,14 @@ +package org.prebid.server.functional.model.response.get + +import groovy.transform.ToString +import org.prebid.server.functional.model.response.BidderError +import org.prebid.server.functional.model.response.Debug +import org.prebid.server.functional.model.response.auction.ErrorType + +@ToString(includeNames = true, ignoreNulls = true) +class GeneralGetResponseExt { + + Debug debug + Map> errors + Map> warnings +} diff --git a/src/test/groovy/org/prebid/server/functional/service/PrebidServerService.groovy b/src/test/groovy/org/prebid/server/functional/service/PrebidServerService.groovy index 9e760b7173e..45ce0627cc7 100644 --- a/src/test/groovy/org/prebid/server/functional/service/PrebidServerService.groovy +++ b/src/test/groovy/org/prebid/server/functional/service/PrebidServerService.groovy @@ -13,6 +13,7 @@ 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.get.GeneralGetRequest 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.vtrack.VtrackRequest @@ -24,6 +25,7 @@ import org.prebid.server.functional.model.response.biddersparams.BiddersParamsRe import org.prebid.server.functional.model.response.cookiesync.CookieSyncResponse import org.prebid.server.functional.model.response.cookiesync.RawCookieSyncResponse import org.prebid.server.functional.model.response.currencyrates.CurrencyRatesResponse +import org.prebid.server.functional.model.response.get.GeneralGetResponse 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 @@ -46,6 +48,7 @@ import static java.time.ZoneOffset.UTC class PrebidServerService implements ObjectMapperWrapper { static final String AUCTION_ENDPOINT = "/openrtb2/auction" + static final String GENERAL_GET_ENDPOINT = "/openrtb2/get" static final String AMP_ENDPOINT = "/openrtb2/amp" static final String COOKIE_SYNC_ENDPOINT = "/cookie_sync" static final String SET_UID_ENDPOINT = "/setuid" @@ -95,11 +98,11 @@ class PrebidServerService implements ObjectMapperWrapper { } } - AmpResponse sendAmpRequestWithAdditionalQueries(AmpRequest ampRequest, Map queries = [:]) { - def response = getAmp(ampRequest, [:], queries) + BidResponse sendGeneralGetRequest(GeneralGetRequest request, Map headers = [:]) { + def response = getAuction(request, headers) checkResponseStatusCode(response) - decode(response.body.asString(), AmpResponse) + decode(response.body.asString(), BidResponse) } AmpResponse sendAmpRequest(AmpRequest ampRequest, Map headers = [:]) { @@ -341,20 +344,22 @@ class PrebidServerService implements ObjectMapperWrapper { requestSpecification.post(COOKIE_SYNC_ENDPOINT) } - private Response getAmp(AmpRequest ampRequest, - Map headers = [:], - Map queries = [:]) { + private Response getAmp(AmpRequest ampRequest, Map headers = [:]) { def map = toMap(ampRequest) - if (!queries.isEmpty()) { - map.putAll(queries) - } - given(requestSpecification).headers(headers) .queryParams(map) .get(AMP_ENDPOINT) } + private Response getAuction(GeneralGetRequest request, Map headers = [:]) { + def map = toMap(request) + + given(requestSpecification).headers(headers) + .queryParams(map) + .get(GENERAL_GET_ENDPOINT) + } + private void checkResponseStatusCode(Response response, int statusCode = 200) { def responseStatusCode = response.statusCode if (responseStatusCode != statusCode) { diff --git a/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy index 220585f008f..0c0702b84bd 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy @@ -1017,7 +1017,7 @@ class BidderParamsSpec extends BaseSpec { and: "Response should contain seatBid" assert response.seatbid.bid.flatten().size() == 1 - and: "Bidder request should be valid" + and: "PBS should preform bidder request" assert bidder.getBidderRequest(bidRequest.id) and: "Response shouldn't contain error" @@ -1058,7 +1058,7 @@ class BidderParamsSpec extends BaseSpec { and: "Response should contain seatBid" assert response.seatbid.bid.flatten().size() == 1 - and: "Bidder request should be valid" + and: "PBS should preform bidder request" assert bidder.getBidderRequest(bidRequest.id) and: "Response shouldn't contain error" @@ -1094,7 +1094,7 @@ class BidderParamsSpec extends BaseSpec { and: "Response should contain seatBid" assert response.seatbid.bid.flatten().size() == 1 - and: "Bidder request should be valid" + and: "PBS should preform bidder request" assert bidder.getBidderRequest(bidRequest.id) and: "Response shouldn't contain error" @@ -1182,7 +1182,7 @@ class BidderParamsSpec extends BaseSpec { and: "Response should contain seatBid" assert response.seatbid.bid.flatten().size() == 1 - and: "Bidder request should be valid" + and: "PBS should preform bidder request" assert bidder.getBidderRequest(bidRequest.id) and: "Response shouldn't contain error" @@ -1390,7 +1390,7 @@ class BidderParamsSpec extends BaseSpec { then: "Response should contain adapter code" assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [BidderName.GENERIC] - and: "Bidder request should be valid" + and: "PBS should preform bidder request" assert bidder.getBidderRequest(bidRequest.id) } @@ -1424,7 +1424,7 @@ class BidderParamsSpec extends BaseSpec { and: "Response should contain repose millis with corresponding bidder" assert response.ext.responsetimemillis.containsKey(ALIAS.value) - and: "Bidder request should be valid" + and: "PBS should preform bidder request" assert bidder.getBidderRequests(bidRequest.id) } @@ -1462,7 +1462,7 @@ class BidderParamsSpec extends BaseSpec { and: "Response should contain repose millis with corresponding bidder" assert response.ext.responsetimemillis.containsKey(ALIAS.value) - and: "Bidder request should be valid" + and: "PBS should preform bidder request" assert bidder.getBidderRequests(bidRequest.id) cleanup: "Stop and remove pbs container" @@ -1515,7 +1515,7 @@ class BidderParamsSpec extends BaseSpec { and: "Response should contain repose millis with corresponding bidder" assert response.ext.responsetimemillis.containsKey(ALIAS.value) - and: "Bidder request should be valid" + and: "PBS should preform bidder request" assert bidder.getBidderRequests(bidRequest.id) cleanup: "Stop and remove pbs container" @@ -1552,7 +1552,7 @@ class BidderParamsSpec extends BaseSpec { and: "Response should contain repose millis with corresponding bidder" assert response.ext.responsetimemillis.containsKey(ALIAS_UPPER_CASE.value) - and: "Bidder request should be valid" + and: "PBS should preform bidder request" assert bidder.getBidderRequests(bidRequest.id) } @@ -1612,7 +1612,7 @@ class BidderParamsSpec extends BaseSpec { and: "Response should contain repose millis with corresponding bidder" assert response.ext.responsetimemillis.containsKey(ALIAS_UPPER_CASE.value) - and: "Bidder request should be valid" + and: "PBS should preform bidder request" assert bidder.getBidderRequests(bidRequest.id) } @@ -1652,7 +1652,7 @@ class BidderParamsSpec extends BaseSpec { and: "Response should contain repose millis with corresponding bidder" assert response.ext.responsetimemillis.containsKey(ALIAS.value) - and: "Bidder request should be valid" + and: "PBS should preform bidder request" assert bidder.getBidderRequests(bidRequest.id) cleanup: "Stop and remove pbs container" @@ -1695,7 +1695,7 @@ class BidderParamsSpec extends BaseSpec { and: "Response should contain repose millis with corresponding bidder" assert response.ext.responsetimemillis.containsKey(ALIAS.value) - and: "Bidder request should be valid" + and: "PBS should preform bidder request" assert bidder.getBidderRequests(bidRequest.id) cleanup: "Stop and remove pbs container" @@ -1735,7 +1735,7 @@ class BidderParamsSpec extends BaseSpec { and: "Response should contain repose millis with corresponding bidder" assert response.ext.responsetimemillis.containsKey(AMX.value) - and: "Bidder request should be valid" + and: "PBS should preform bidder request" assert bidder.getBidderRequests(bidRequest.id) cleanup: "Stop and remove pbs container" diff --git a/src/test/groovy/org/prebid/server/functional/tests/GeneralGetInterfaceImpSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/GeneralGetInterfaceImpSpec.groovy new file mode 100644 index 00000000000..7c38590340d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/GeneralGetInterfaceImpSpec.groovy @@ -0,0 +1,1746 @@ +package org.prebid.server.functional.tests + +import org.prebid.server.functional.model.db.StoredRequest +import org.prebid.server.functional.model.request.amp.Targeting +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.Format +import org.prebid.server.functional.model.request.auction.Imp +import org.prebid.server.functional.model.request.auction.VideoPlacementSubtypes +import org.prebid.server.functional.model.request.auction.VideoPlcmtSubtype +import org.prebid.server.functional.model.request.get.GeneralGetRequest +import org.prebid.server.functional.model.response.auction.BidResponse +import org.prebid.server.functional.model.response.auction.MediaType +import org.prebid.server.functional.service.PrebidServerException +import org.prebid.server.functional.util.PBSUtils +import spock.lang.PendingFeature + +import java.nio.charset.StandardCharsets +import java.time.Instant + +import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID + +class GeneralGetInterfaceImpSpec extends BaseSpec { + + def "PBS should apply mimes from general get request when it's specified"() { + given: "Default General get request" + def mimes = PBSUtils.randomString + def generalGetRequest = GeneralGetRequest.default.tap { + it.mimes = [mimes] + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain mimes from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.size() == 1 + assert bidderRequest.imp.first.singleMediaTypeData.mimes == [mimes] + + where: + impMediaType << [MediaType.BANNER, MediaType.VIDEO, MediaType.AUDIO] + } + + @PendingFeature + def "PBS should remove invalid mimes from general get request when it's specified"() { + given: "Default General get request" + def validMimes = PBSUtils.randomString + def generalGetRequest = GeneralGetRequest.default.tap { + it.mimes = (invalidMimes + validMimes) + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + + and: "Response should contain warning" + assert response.ext?.warnings[PREBID]*.code == [999] + assert response.ext?.warnings[PREBID]*.message == ['some message'] //TODO replace + + and: "Bidder request should contain mimes from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.size() == 1 + assert bidderRequest.imp.first.singleMediaTypeData.mimes == [validMimes] + + where: + invalidMimes << [[''], [PBSUtils.randomNumber.toString()]] + } + + def "PBS should apply width and height for banner imp from general get request when it's specified"() { + given: "Default General get request" + def width = PBSUtils.randomNumber + def height = PBSUtils.randomNumber + def generalGetRequest = (bannerGeneralRequest(height, width) as GeneralGetRequest).tap { + it.storedRequestId = PBSUtils.randomNumber + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.BANNER)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain width and height from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.size() == 1 + verifyAll(bidderRequest.imp.first.banner) { + it.width == width + it.height == height + it.format.width == [width] + it.format.height == [height] + } + + where: + bannerGeneralRequest << + [ + { Integer h, Integer w -> new GeneralGetRequest(height: h, width: w) }, + { Integer h, Integer w -> new GeneralGetRequest(overrideHeight: h, overrideWidth: w) }, + { Integer h, Integer w -> + new GeneralGetRequest(height: PBSUtils.randomNumber, width: PBSUtils.randomNumber, + overrideHeight: h, overrideWidth: w) + } + ] + } + + def "PBS should unsuccessfully pass and throw error due to validation banner.{w.h} when values {w.h} is null"() { + given: "Default General get request" + def generalGetRequest = GeneralGetRequest.default.tap { + it.width = bannerFormatWidth + it.height = bannerFormatHeight + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.BANNER).tap { it.banner.format = null }] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "PBS should throw error due to invalid request" + def exception = thrown(PrebidServerException) + assert exception.statusCode == 400 + assert exception.responseBody == 'Invalid request format: request.imp[0].banner has no sizes. Define "w" and "h", or include "format" elements' + + where: + bannerFormatWidth | bannerFormatHeight + 0 | null + null | null + null | PBSUtils.randomNegativeNumber + } + + def "PBS should unsuccessfully pass and throw error due to validation banner.{w.h} when values {w.h} not null"() { + given: "Default General get request" + def generalGetRequest = GeneralGetRequest.default.tap { + it.width = bannerFormatWidth + it.height = bannerFormatHeight + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.BANNER).tap { it.banner.format = null }] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "PBS should throw error due to invalid request" + def exception = thrown(PrebidServerException) + assert exception.statusCode == 400 + assert exception.responseBody == 'Invalid request format: request.imp[0].banner must define a valid "h" and "w" properties' + + where: + bannerFormatWidth | bannerFormatHeight + 0 | 0 + 0 | PBSUtils.randomNegativeNumber + PBSUtils.randomNegativeNumber | PBSUtils.randomNegativeNumber + } + + def "PBS should apply width and height for banner imp from general get request when banner width and height are square dimensions"() { + given: "Default General get request" + def side = PBSUtils.getRandomNumber(1, 10) + def generalGetRequest = GeneralGetRequest.default.tap { + it.width = side + it.height = side + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.BANNER)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain width and height from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.size() == 1 + verifyAll(bidderRequest.imp.first.banner) { + it.width == side + it.height == side + it.format.width == [side] + it.format.height == [side] + } + } + + def "PBS should apply width and height for video imp from general get request when it's specified"() { + given: "Default General get request" + def width = PBSUtils.randomNumber + def height = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.width = width + it.height = height + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.VIDEO)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain width and height from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.size() == 1 + verifyAll(bidderRequest.imp.first.video) { + it.width == width + it.height == height + } + } + + def "PBS should apply sizes banner imp from general get request when it's specified"() { + given: "Default General get request" + def width = PBSUtils.randomNumber + def height = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.sizes = ["${width}x${height}".toString()] + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.BANNER)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain width and height from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.size() == 1 + assert bidderRequest.imp.first.banner.format.width == [width] + assert bidderRequest.imp.first.banner.format.height == [height] + } + + def "PBS should apply sizes legacy banner imp from general get request when it's specified"() { + given: "Default General get request" + def width = PBSUtils.randomNumber + def height = PBSUtils.randomNumber + def widthSecond = PBSUtils.randomNumber + def heightSecond = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.sizesLegacy = ["${width}x${height},${widthSecond}x${heightSecond}".toString()] + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.BANNER)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain width and height from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.size() == 1 + assert bidderRequest.imp.first.banner.format.width == [width, widthSecond] + assert bidderRequest.imp.first.banner.format.height == [height, heightSecond] + } + + def "PBS should apply sizes banner imp from general get request when banner width and height are square dimensions"() { + given: "Default General get request" + def side = PBSUtils.getRandomNumber(1, 10) + def generalGetRequest = (bannerGeneralRequest(side) as GeneralGetRequest).tap { + it.storedRequestId = PBSUtils.randomNumber + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.BANNER)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain width and height from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.size() == 1 + assert bidderRequest.imp.first.banner.format.width == [side] + assert bidderRequest.imp.first.banner.format.height == [side] + + where: + bannerGeneralRequest << + [ + { Integer requestSide -> new GeneralGetRequest(sizes: ["${requestSide}x${requestSide}".toString()]) }, + { Integer requestSide -> new GeneralGetRequest(sizesLegacy: ["${requestSide}x${requestSide}".toString()]) }, + { Integer requestSide -> + new GeneralGetRequest(sizes: ["${requestSide}x${requestSide}".toString()], + sizesLegacy: ["${PBSUtils.randomNumber}x${PBSUtils.randomNumber}".toString()]) + } + ] + } + + def "PBS should ignore sizes banner imp from general get request when it's specified partially"() { + given: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.BANNER)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain width and height from original stored request" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.size() == 1 + assert bidderRequest.imp.banner.format.width == request.imp.banner.format.width + assert bidderRequest.imp.banner.format.height == request.imp.banner.format.height + + where: + generalGetRequest << [ + GeneralGetRequest.default.tap { sizes = ['0x'] }, + GeneralGetRequest.default.tap { sizes = ['x'] }, + GeneralGetRequest.default.tap { sizes = ['0'] }, + GeneralGetRequest.default.tap { sizes = [''] }, + GeneralGetRequest.default.tap { sizes = [' '] }, + GeneralGetRequest.default.tap { sizes = [null] }, + GeneralGetRequest.default.tap { sizes = ["${PBSUtils.randomNegativeNumber}x".toString()] }, + ] + } + + def "PBS should unsuccessfully pass and throw error due to validation banner sizes when sizes width and height is invalid"() { + given: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.BANNER)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "PBS should throw error due to invalid request" + def exception = thrown(PrebidServerException) + assert exception.statusCode == 400 + assert exception.responseBody == 'Invalid request format: request.imp[0].banner.format[0] should define *either* ' + + '{w, h} (for static size requirements) *or* {wmin, wratio, hratio} (for flexible sizes) to be non-zero positive' + + where: + generalGetRequest << [ + GeneralGetRequest.default.tap { sizes = ['0x0'] }, + GeneralGetRequest.default.tap { sizes = ["0x${PBSUtils.randomNegativeNumber}".toString()] }, + GeneralGetRequest.default.tap { sizes = ["x${PBSUtils.randomNegativeNumber}".toString()] }, + GeneralGetRequest.default.tap { sizes = ["${PBSUtils.randomNegativeNumber}x${PBSUtils.randomNegativeNumber}".toString()] }, + ] + } + + def "PBS should apply slot from general get request when it's specified"() { + given: "Default General get request" + def slotParam = PBSUtils.randomString + def generalGetRequest = GeneralGetRequest.default.tap { + it.slot = slotParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain tagId from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.tagId.flatten() == [slotParam] + } + + def "PBS should apply duration from general get request when it's specified"() { + given: "Default General get request" + def minDurationParam = PBSUtils.randomNumber + def maxDurationParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.minDuration = minDurationParam + it.maxDuration = maxDurationParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain duration from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.singleMediaTypeData.minduration == [minDurationParam] + assert bidderRequest.imp.singleMediaTypeData.maxduration == [maxDurationParam] + + where: + impMediaType << [MediaType.VIDEO, MediaType.AUDIO] + } + + def "PBS should apply api from general get request when it's specified"() { + given: "Default General get request" + def apiParam = [PBSUtils.randomNumber, PBSUtils.randomNumber] + def generalGetRequest = GeneralGetRequest.default.tap { + it.api = apiParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain api from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.singleMediaTypeData.api == apiParam + + where: + impMediaType << [MediaType.BANNER, MediaType.VIDEO, MediaType.AUDIO] + } + + def "PBS should apply battr from general get request when it's specified"() { + given: "Default General get request" + def battrParam = [PBSUtils.randomNumber] + def generalGetRequest = GeneralGetRequest.default.tap { + it.blockAttributes = battrParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain battr from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.singleMediaTypeData.battr == battrParam + + where: + impMediaType << [MediaType.BANNER, MediaType.VIDEO, MediaType.AUDIO] + } + + def "PBS should apply delivery from general get request when it's specified"() { + given: "Default General get request" + def deliveryParam = [PBSUtils.randomNumber] + def generalGetRequest = GeneralGetRequest.default.tap { + it.delivery = deliveryParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain delivery from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.singleMediaTypeData.delivery == deliveryParam + + where: + impMediaType << [MediaType.VIDEO, MediaType.AUDIO] + } + + def "PBS should apply linearity from general get request when it's specified"() { + given: "Default General get request" + def linearityParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.linearity = linearityParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.VIDEO)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain linearity from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.video.linearity == linearityParam + } + + def "PBS should apply minbr and maxbr from general get request when it's specified"() { + given: "Default General get request" + def minBitrateParam = PBSUtils.randomNumber + def maxBitrateParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.minBitrate = minBitrateParam + it.maxBitrate = maxBitrateParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain minbr from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.singleMediaTypeData.minbitrate == minBitrateParam + assert bidderRequest.imp.first.singleMediaTypeData.maxbitrate == maxBitrateParam + + where: + impMediaType << [MediaType.VIDEO, MediaType.AUDIO] + } + + def "PBS should apply maxex from general get request when it's specified"() { + given: "Default General get request" + def maxexParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.maxExtended = maxexParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain maxex from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.singleMediaTypeData.maxextended == maxexParam + + where: + impMediaType << [MediaType.VIDEO, MediaType.AUDIO] + } + + def "PBS should apply maxseq from general get request when it's specified"() { + given: "Default General get request" + def maxseqParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.maxSequence = maxseqParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain maxseq from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.singleMediaTypeData.maxseq == maxseqParam + + where: + impMediaType << [MediaType.VIDEO, MediaType.AUDIO] + } + + def "PBS should apply minCpmPerSec from general get request when it's specified"() { + given: "Default General get request" + def mincpmsParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.minCpmPerSec = mincpmsParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain mincpms from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.singleMediaTypeData.mincpmpersec == mincpmsParam + + where: + impMediaType << [MediaType.VIDEO, MediaType.AUDIO] + } + + def "PBS should apply poddur from general get request when it's specified"() { + given: "Default General get request" + def poddurParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.podDuration = poddurParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain poddur from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.singleMediaTypeData.poddur == poddurParam + + where: + impMediaType << [MediaType.VIDEO, MediaType.AUDIO] + } + + def "PBS should apply podId from general get request when it's specified"() { + given: "Default General get request" + def podIdParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.podId = podIdParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain podid from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.singleMediaTypeData.podid == podIdParam + + where: + impMediaType << [MediaType.VIDEO, MediaType.AUDIO] + } + + def "PBS should apply podseq from general get request when it's specified"() { + given: "Default General get request" + def podSequenceParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.podSequence = podSequenceParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain podseq from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.singleMediaTypeData.podseq == podSequenceParam + + where: + impMediaType << [MediaType.VIDEO, MediaType.AUDIO] + } + + def "PBS should apply proto from general get request when it's specified"() { + given: "Default General get request" + def protoParam = [PBSUtils.randomNumber, PBSUtils.randomNumber] + def generalGetRequest = GeneralGetRequest.default.tap { + it.protocols = protoParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain proto from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.singleMediaTypeData.protocols == protoParam + + where: + impMediaType << [MediaType.VIDEO, MediaType.AUDIO] + } + + def "PBS should apply rqddurs from general get request when it's specified"() { + given: "Default General get request" + def requiredDurationsParam = [PBSUtils.randomNumber, PBSUtils.randomNumber] + def generalGetRequest = GeneralGetRequest.default.tap { + it.requiredDurations = requiredDurationsParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain rqddurs from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.singleMediaTypeData.rqddurs == requiredDurationsParam + + where: + impMediaType << [MediaType.VIDEO, MediaType.AUDIO] + } + + def "PBS should apply sequence from general get request when it's specified"() { + given: "Default General get request" + def sequenceParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.sequence = sequenceParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain seq from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.singleMediaTypeData.sequence == sequenceParam + + where: + impMediaType << [MediaType.VIDEO, MediaType.AUDIO] + } + + def "PBS should apply slotInPod from general get request when it's specified"() { + given: "Default General get request" + def slotInPodParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.slotInPod = slotInPodParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain slotinpod from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.singleMediaTypeData.slotinpod == slotInPodParam + + where: + impMediaType << [MediaType.VIDEO, MediaType.AUDIO] + } + + def "PBS should apply startdelay from general get request when it's specified"() { + given: "Default General get request" + def startDelayParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.startDelay = startDelayParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain startdelay from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.singleMediaTypeData.startdelay == startDelayParam + + where: + impMediaType << [MediaType.VIDEO, MediaType.AUDIO] + } + + def "PBS should apply skip from general get request when it's specified"() { + given: "Default General get request" + def skipParam = PBSUtils.randomNumber + def skipAfterParam = PBSUtils.randomNumber + def skipMinParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.skip = skipParam + it.skipAfter = skipAfterParam + it.skipMin = skipMinParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.VIDEO)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain skip from param" + def bidderRequest = bidder.getBidderRequest(request.id) + verifyAll(bidderRequest.imp.first.video) { + it.skip == skipParam + it.skipafter == skipAfterParam + it.skipmin == skipMinParam + } + } + + def "PBS should apply position from general get request when it's specified"() { + given: "Default General get request" + def positionParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.position = positionParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(impMediaType)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain pos from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.singleMediaTypeData.pos == positionParam + + where: + impMediaType << [MediaType.BANNER, MediaType.VIDEO] + } + + def "PBS should apply stitched from general get request when it's specified"() { + given: "Default General get request" + def stitchedParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.stitched = stitchedParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.AUDIO)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain stitched from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.audio.stitched == stitchedParam + } + + def "PBS should apply feed from general get request when it's specified"() { + given: "Default General get request" + def feedParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.feed = feedParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.AUDIO)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain feed from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.audio.feed == feedParam + } + + def "PBS should apply normalizedVolume from general get request when it's specified"() { + given: "Default General get request" + def normalizedVolumeParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.normalizedVolume = normalizedVolumeParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.AUDIO)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain nvol from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.audio.nvol == normalizedVolumeParam + } + + def "PBS should apply placement from general get request when it's specified"() { + given: "Default General get request" + def placementParam = PBSUtils.getRandomEnum(VideoPlacementSubtypes) + def placementSubtypeParam = PBSUtils.getRandomEnum(VideoPlcmtSubtype) + def generalGetRequest = GeneralGetRequest.default.tap { + it.placement = placementParam + it.placementSubtype = placementSubtypeParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.VIDEO)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain placement from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.video.placement == placementParam + assert bidderRequest.imp.first.video.plcmt == placementSubtypeParam + } + + def "PBS should apply playbackendParam from general get request when it's specified"() { + given: "Default General get request" + def playbackEndParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.playbackEnd = playbackEndParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.VIDEO)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain playbackend from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.video.playbackend == playbackEndParam + } + + def "PBS should apply playbackMethodParam from general get request when it's specified"() { + given: "Default General get request" + def playbackMethodParam = [PBSUtils.randomNumber, PBSUtils.randomNumber] + def generalGetRequest = GeneralGetRequest.default.tap { + it.playbackMethods = playbackMethodParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.VIDEO)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain playbackmethod from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.video.playbackmethod == playbackMethodParam + } + + def "PBS should apply boxingAllowedParam from general get request when it's specified"() { + given: "Default General get request" + def boxingAllowedParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.boxingAllowed = boxingAllowedParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.VIDEO)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain boxingallowed from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.video.boxingallowed == boxingAllowedParam + } + + def "PBS should apply btype from general get request when it's specified"() { + given: "Default General get request" + def btypeParam = [PBSUtils.randomNumber, PBSUtils.randomNumber] + def generalGetRequest = GeneralGetRequest.default.tap { + it.bannerTypes = btypeParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.BANNER)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain btype from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.banner.btype == btypeParam + } + + def "PBS should apply expandableDirections from general get request when it's specified"() { + given: "Default General get request" + def expandableDirectionsParam = [PBSUtils.randomNumber, PBSUtils.randomNumber] + def generalGetRequest = GeneralGetRequest.default.tap { + it.expandableDirections = expandableDirectionsParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.BANNER)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain expdir from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.banner.expandableDirections == expandableDirectionsParam + } + + def "PBS should apply topFrame from general get request when it's specified"() { + given: "Default General get request" + def topFrameParam = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.topFrame = topFrameParam + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [Imp.getDefaultImpression(MediaType.BANNER)] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain topFrame from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.imp.first.banner.topframe == topFrameParam + } + + def "PBS should use original values for banner imp when it's not specified in get request"() { + given: "Default General get request" + def storedRequestId = PBSUtils.randomString + def generalGetRequest = new GeneralGetRequest(storedRequestId: storedRequestId) + + and: "Default stored request" + def bannerImp = Imp.getDefaultImpression(MediaType.BANNER).tap { + it.banner.mimes = [PBSUtils.randomString] + it.banner.width = PBSUtils.randomNumber + it.banner.height = PBSUtils.randomNumber + it.banner.format = [new Format(width: PBSUtils.randomNumber, height: PBSUtils.randomNumber)] + it.banner.api = [PBSUtils.randomNumber] + it.banner.battr = [PBSUtils.randomNumber] + it.banner.pos = PBSUtils.randomNumber + it.banner.btype = [PBSUtils.randomNumber] + it.banner.expandableDirections = [PBSUtils.randomNumber] + it.banner.topframe = PBSUtils.randomNumber + } + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [bannerImp] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain imp data from original request" + verifyAll(bidder.getBidderRequest(request.id).imp.first.banner) { + it.mimes == bannerImp.banner.mimes + it.width == bannerImp.banner.width + it.height == bannerImp.banner.height + it.format == bannerImp.banner.format + it.api == bannerImp.banner.api + it.battr == bannerImp.banner.battr + it.pos == bannerImp.banner.pos + it.btype == bannerImp.banner.btype + it.expandableDirections == bannerImp.banner.expandableDirections + it.topframe == bannerImp.banner.topframe + } + } + + def "PBS should use original values for video imp when it's not specified in get request"() { + given: "Default General get request" + def storedRequestId = PBSUtils.randomString + def generalGetRequest = new GeneralGetRequest(storedRequestId: storedRequestId) + + and: "Default stored request" + def videoImp = Imp.getDefaultImpression(MediaType.VIDEO).tap { + it.video.mimes = [PBSUtils.randomString] + it.video.width = PBSUtils.randomNumber + it.video.height = PBSUtils.randomNumber + it.video.minduration = PBSUtils.randomNumber + it.video.maxduration = PBSUtils.randomNumber + it.video.api = [PBSUtils.randomNumber] + it.video.battr = [PBSUtils.randomNumber] + it.video.delivery = [PBSUtils.randomNumber] + it.video.linearity = PBSUtils.randomNumber + it.video.minbitrate = PBSUtils.randomNumber + it.video.maxbitrate = PBSUtils.randomNumber + it.video.maxextended = PBSUtils.randomNumber + it.video.maxseq = PBSUtils.randomNumber + it.video.mincpmpersec = PBSUtils.randomNumber + it.video.poddur = PBSUtils.randomNumber + it.video.podid = PBSUtils.randomNumber + it.video.podseq = PBSUtils.randomNumber + it.video.protocols = [PBSUtils.randomNumber] + it.video.rqddurs = [PBSUtils.randomNumber] + it.video.sequence = PBSUtils.randomNumber + it.video.slotinpod = PBSUtils.randomNumber + it.video.startdelay = PBSUtils.randomNumber + it.video.skip = PBSUtils.randomNumber + it.video.skipafter = PBSUtils.randomNumber + it.video.skipmin = PBSUtils.randomNumber + it.video.pos = PBSUtils.randomNumber + it.video.placement = PBSUtils.getRandomEnum(VideoPlacementSubtypes) + it.video.plcmt = PBSUtils.getRandomEnum(VideoPlcmtSubtype) + it.video.playbackend = PBSUtils.randomNumber + it.video.playbackmethod = [PBSUtils.randomNumber] + it.video.boxingallowed = PBSUtils.randomNumber + } + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [videoImp] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain imp data from original request" + verifyAll(bidder.getBidderRequest(request.id).imp.first.video) { + it.mimes == videoImp.video.mimes + it.width == videoImp.video.width + it.height == videoImp.video.height + it.minduration == videoImp.video.minduration + it.maxduration == videoImp.video.maxduration + it.api == videoImp.video.api + it.battr == videoImp.video.battr + it.delivery == videoImp.video.delivery + it.linearity == videoImp.video.linearity + it.minbitrate == videoImp.video.minbitrate + it.maxbitrate == videoImp.video.maxbitrate + it.maxextended == videoImp.video.maxextended + it.maxseq == videoImp.video.maxseq + it.mincpmpersec == videoImp.video.mincpmpersec + it.poddur == videoImp.video.poddur + it.podid == videoImp.video.podid + it.podseq == videoImp.video.podseq + it.protocols == videoImp.video.protocols + it.rqddurs == videoImp.video.rqddurs + it.sequence == videoImp.video.sequence + it.slotinpod == videoImp.video.slotinpod + it.startdelay == videoImp.video.startdelay + it.skip == videoImp.video.skip + it.skipafter == videoImp.video.skipafter + it.skipmin == videoImp.video.skipmin + it.pos == videoImp.video.pos + it.placement == videoImp.video.placement + it.plcmt == videoImp.video.plcmt + it.playbackend == videoImp.video.playbackend + it.playbackmethod == videoImp.video.playbackmethod + it.boxingallowed == videoImp.video.boxingallowed + } + } + + def "PBS should use original values for audio imp when it's not specified in get request"() { + given: "Default General get request" + def storedRequestId = PBSUtils.randomString + def generalGetRequest = new GeneralGetRequest(storedRequestId: storedRequestId) + + and: "Default stored request" + def audioImp = Imp.getDefaultImpression(MediaType.AUDIO).tap { + it.audio.mimes = [PBSUtils.randomString] + it.audio.minduration = PBSUtils.randomNumber + it.audio.maxduration = PBSUtils.randomNumber + it.audio.api = [PBSUtils.randomNumber] + it.audio.battr = [PBSUtils.randomNumber] + it.audio.delivery = [PBSUtils.randomNumber] + it.audio.minbitrate = PBSUtils.randomNumber + it.audio.maxbitrate = PBSUtils.randomNumber + it.audio.maxextended = PBSUtils.randomNumber + it.audio.maxseq = PBSUtils.randomNumber + it.audio.mincpmpersec = PBSUtils.randomNumber + it.audio.poddur = PBSUtils.randomNumber + it.audio.podid = PBSUtils.randomNumber + it.audio.podseq = PBSUtils.randomNumber + it.audio.protocols = [PBSUtils.randomNumber] + it.audio.rqddurs = [PBSUtils.randomNumber] + it.audio.sequence = PBSUtils.randomNumber + it.audio.slotinpod = PBSUtils.randomNumber + it.audio.startdelay = PBSUtils.randomNumber + it.audio.stitched = PBSUtils.randomNumber + it.audio.feed = PBSUtils.randomNumber + it.audio.nvol = PBSUtils.randomNumber + } + def request = BidRequest.getDefaultBidRequest().tap { + it.imp = [audioImp] + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(request) + bidder.setResponse(request.id, bidResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain imp data from original request" + verifyAll(bidder.getBidderRequest(request.id).imp.first.audio) { + it.mimes == audioImp.audio.mimes + it.minduration == audioImp.audio.minduration + it.maxduration == audioImp.audio.maxduration + it.api == audioImp.audio.api + it.battr == audioImp.audio.battr + it.delivery == audioImp.audio.delivery + it.minbitrate == audioImp.audio.minbitrate + it.maxbitrate == audioImp.audio.maxbitrate + it.maxextended == audioImp.audio.maxextended + it.maxseq == audioImp.audio.maxseq + it.mincpmpersec == audioImp.audio.mincpmpersec + it.poddur == audioImp.audio.poddur + it.podid == audioImp.audio.podid + it.podseq == audioImp.audio.podseq + it.protocols == audioImp.audio.protocols + it.rqddurs == audioImp.audio.rqddurs + it.sequence == audioImp.audio.sequence + it.slotinpod == audioImp.audio.slotinpod + it.startdelay == audioImp.audio.startdelay + it.stitched == audioImp.audio.stitched + it.feed == audioImp.audio.feed + it.nvol == audioImp.audio.nvol + } + } + + def "PBS get request should move targeting key to imp.ext.data"() { + given: "Create targeting" + def targeting = new Targeting().tap { + any = PBSUtils.randomString + } + + and: "Encode Targeting to String" + def encodeTargeting = URLEncoder.encode(encode(targeting), StandardCharsets.UTF_8) + + and: "Amp request with targeting" + def generalGetRequest = GeneralGetRequest.default.tap { + it.targeting = encodeTargeting + } + + and: "Default BidRequest" + def bidRequest = BidRequest.defaultBidRequest + + and: "Create and save stored request into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.storedRequestId, bidRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "General get response should contain value from targeting in imp.ext.data" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.imp[0].ext.data.any == targeting.any + } + + def "PBS should throw exception when general get request linked to stored request with several imps"() { + given: "Start time" + def startTime = Instant.now() + + and: "Stored request with several imps" + def request = BidRequest.getDefaultBidRequest().tap { + addImp(Imp.defaultImpression) + setAccountId(generalGetRequest.resolveStoredRequestId()) + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain only first imp data" + assert bidder.getBidderRequest(request.id).imp.id == [request.imp.first.id] + + and: "PBS log should contain message" + def logs = defaultPbsService.getLogsByTime(startTime) + assert getLogsByText(logs, '').size() == 1 //TODO add logs + + where: + generalGetRequest << [ + new GeneralGetRequest(storedRequestId: PBSUtils.randomNumber), + new GeneralGetRequest(storedRequestIdLegacy: PBSUtils.randomNumber) + ] + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/GeneralGetInterfaceRequestSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/GeneralGetInterfaceRequestSpec.groovy new file mode 100644 index 00000000000..19c855e7dde --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/GeneralGetInterfaceRequestSpec.groovy @@ -0,0 +1,886 @@ +package org.prebid.server.functional.tests + +import org.prebid.server.functional.model.bidderspecific.BidderRequest +import org.prebid.server.functional.model.db.StoredRequest +import org.prebid.server.functional.model.db.StoredResponse +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.Content +import org.prebid.server.functional.model.request.auction.DebugCondition +import org.prebid.server.functional.model.request.auction.Device +import org.prebid.server.functional.model.request.auction.DeviceExt +import org.prebid.server.functional.model.request.auction.DeviceType +import org.prebid.server.functional.model.request.auction.PublicCountryIp +import org.prebid.server.functional.model.request.auction.StoredAuctionResponse +import org.prebid.server.functional.model.request.get.GeneralGetRequest +import org.prebid.server.functional.model.response.auction.SeatBid +import org.prebid.server.functional.service.PrebidServerException +import org.prebid.server.functional.util.PBSUtils +import spock.lang.PendingFeature + +import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST +import static org.prebid.server.functional.model.request.auction.DistributionChannel.APP +import static org.prebid.server.functional.model.request.auction.DistributionChannel.DOOH +import static org.prebid.server.functional.model.request.auction.DistributionChannel.SITE +import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID + +class GeneralGetInterfaceRequestSpec extends BaseSpec { + + def "PBS should process bid request from default general get request"() { + given: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "PBS should perform bidder request" + assert bidder.getBidderRequest(request.id) + + where: + generalGetRequest << [ + new GeneralGetRequest(storedRequestId: PBSUtils.randomNumber), + new GeneralGetRequest(storedRequestIdLegacy: PBSUtils.randomNumber) + ] + } + + def "PBS should prioritise new storedRequest param over legacy when both presents"() { + given: "General get request with new and old stored request param" + def generalGetRequest = new GeneralGetRequest(storedRequestId: PBSUtils.randomNumber, + storedRequestIdLegacy: PBSUtils.randomNumber) + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.storedRequestId, request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "PBS should perform bidder request" + assert bidder.getBidderRequest(request.id) + } + + def "PBS should respond with error when process bid request is not specified in general get request"() { + given: "General get request without stored request param" + def generalGetRequest = new GeneralGetRequest() + + when: "PBS processes general get request" + defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Request should fail with an error" + def exception = thrown(PrebidServerException) + assert exception.statusCode == BAD_REQUEST.code() + assert exception.responseBody == "Invalid request format: Request require the stored request id." + } + + def "PBS should apply accountId from general get request when it's specified"() { + given: "Default stored request" + def request = BidRequest.getDefaultBidRequest(distributionType).tap { + setAccountId(generalGetRequest.resolveAccountId()) + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain accountId from param" + assert bidder.getBidderRequest(request.id).accountId == generalGetRequest.resolveAccountId() + + where: + generalGetRequest | distributionType + GeneralGetRequest.default.tap { it.accountId = PBSUtils.randomNumber } | SITE + GeneralGetRequest.default.tap { it.accountId = PBSUtils.randomNumber } | APP + GeneralGetRequest.default.tap { it.accountId = PBSUtils.randomNumber } | DOOH + GeneralGetRequest.default.tap { it.accountIdLegacy = PBSUtils.randomNumber } | SITE + GeneralGetRequest.default.tap { it.accountIdLegacy = PBSUtils.randomNumber } | APP + GeneralGetRequest.default.tap { it.accountIdLegacy = PBSUtils.randomNumber } | DOOH + } + + def "PBS should prioritise new accountId param over legacy when both presents"() { + given: "Default General get request" + def generalGetRequest = GeneralGetRequest.default.tap { + it.accountId = PBSUtils.randomNumber + it.accountIdLegacy = PBSUtils.randomNumber + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + setAccountId(generalGetRequest.accountId) + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain accountId from param" + assert bidder.getBidderRequest(request.id).accountId == generalGetRequest.accountId + } + + def "PBS should apply tmax from general get request when it's specified"() { + given: "Default General get request" + def generalGetRequest = GeneralGetRequest.default.tap { + it.timeoutMax = PBSUtils.getRandomNumber(3000, 5000) + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain tmax from param" + assert PBSUtils.isApproximatelyEqual(bidder.getBidderRequest(request.id).tmax as Integer, generalGetRequest.timeoutMax) + } + + @PendingFeature + def "PBS shouldn't apply tmax from general get request when it's specified lower than 100"() { + given: "Default General get request" + def generalGetRequest = GeneralGetRequest.default.tap { + it.timeoutMax = PBSUtils.getRandomNumber(0, 100) + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should contain warnings" + assert response.ext?.warnings[PREBID]*.message == ["replace"] //TODO replace + + and: "Response should not contain errors and warnings" + assert !response.ext?.errors + + and: "Bidder request should contain tmax from param" + assert bidder.getBidderRequest(request.id).tmax != generalGetRequest.timeoutMax + } + + def "PBS shouldn't apply tmax from general get request when it's specified as negative"() { + given: "Default General get request" + def generalGetRequest = GeneralGetRequest.default.tap { + it.timeoutMax = PBSUtils.randomNegativeNumber + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "PBS should throw error due to invalid request" + def exception = thrown(PrebidServerException) + assert exception.statusCode == 500 + assert exception.responseBody == 'Critical error while running the auction: Start time and timeout must be positive' + } + + def "PBS should apply debug from general get request when it's specified"() { + given: "Default General get request" + def generalGetRequest = GeneralGetRequest.default.tap { + it.debug = debugCondition + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain debug from param" + assert bidder.getBidderRequest(request.id).ext.prebid.debug == debugCondition + + where: + debugCondition << [DebugCondition.DISABLED, DebugCondition.ENABLED] + } + + def "PBS should apply outputformat from general get request when it's specified"() { + given: "Default General get request" + def outputFormat = PBSUtils.randomString + def generalGetRequest = GeneralGetRequest.default.tap { + it.outputFormat = outputFormat + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain outputformat from param" + assert bidder.getBidderRequest(request.id).ext.prebid.outputFormat == outputFormat + } + + def "PBS should apply outputmodule from general get request when it's specified"() { + given: "Default General get request" + def outputModule = PBSUtils.randomString + def generalGetRequest = GeneralGetRequest.default.tap { + it.outputModule = outputModule + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain outputModule from param" + assert bidder.getBidderRequest(request.id).ext.prebid.outputModule == outputModule + } + + def "PBS should apply storedAuctionResponse from general get request when it's specified"() { + given: "Default General get request" + def storedAuctionResponseId = PBSUtils.randomString + def generalGetRequest = GeneralGetRequest.default.tap { + it.storedAuctionResponseId = storedAuctionResponseId + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.storedRequestId, request) + storedRequestDao.save(storedRequest) + + and: "Stored response in DB" + def storedAuctionResponse = SeatBid.getStoredResponse(request) + def storedResponse = new StoredResponse(responseId: storedAuctionResponseId, + storedAuctionResponse: storedAuctionResponse) + storedResponseDao.save(storedResponse) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should contain same stored auction response as requested" + assert response.seatbid == [storedAuctionResponse] + + and: "PBS should emit warning" + assert response.ext?.warnings[PREBID]*.code == [999] + assert response.ext?.warnings[PREBID]*.message == + ["no auction. response defined by storedauctionresponse" as String] + + and: "Response should not contain errors" + assert !response.ext?.errors + + and: "PBS not send request to bidder" + assert bidder.getRequestCount(request.id) == 0 + } + + def "PBS should apply dnt from general get request when it's specified"() { + given: "Default General get request" + def dnt = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.doNotTrack = dnt + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain dnt from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.device.dnt == dnt + } + + def "PBS should apply lmt from general get request when it's specified"() { + given: "Default General get request" + def lmt = PBSUtils.randomNumber + def generalGetRequest = GeneralGetRequest.default.tap { + it.limitAdTracking = lmt + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain lmt from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.device.lmt == lmt + } + + def "PBS should apply bcat from general get request when it's specified"() { + given: "Default General get request" + def bcat = [PBSUtils.randomString] + def generalGetRequest = GeneralGetRequest.default.tap { + it.blockedCategories = bcat + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain bcat from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.bcat == bcat + } + + def "PBS should apply badv from general get request when it's specified"() { + given: "Default General get request" + def badv = [PBSUtils.randomString] + def generalGetRequest = GeneralGetRequest.default.tap { + it.blockedAdvertisers = badv + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain badv from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.badv == badv + } + + def "PBS should apply page from general get request when it's specified"() { + given: "Default General get request" + def page = PBSUtils.randomString + def generalGetRequest = GeneralGetRequest.default.tap { + it.page = page + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain page from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.site.page == page + } + + def "PBS should apply bundle from general app bundle request when it's specified"() { + given: "Default General get request" + def bundle = PBSUtils.randomString + def generalGetRequest = GeneralGetRequest.default.tap { + it.appBundle = bundle + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest(APP) + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain app bundle from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.app.bundle == bundle + } + + def "PBS should apply name from general app name request when it's specified"() { + given: "Default General get request" + def name = PBSUtils.randomString + def generalGetRequest = GeneralGetRequest.default.tap { + it.appName = name + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest(APP) + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain app name from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.app.name == name + } + + def "PBS should apply name from general app storeUrl request when it's specified"() { + given: "Default General get request" + def storeUrl = PBSUtils.randomString + def generalGetRequest = GeneralGetRequest.default.tap { + it.storeUrl = storeUrl + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest(APP) + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain app storeUrl from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.app.storeUrl == storeUrl + } + + def "PBS should apply distribution data from general get request when it's specified"() { + given: "Default General get request" + def generalGetRequest = GeneralGetRequest.default.tap { + it.contentGenre = PBSUtils.randomString + it.contentLanguage = PBSUtils.randomString + it.contentRating = PBSUtils.randomString + it.contentCategory = PBSUtils.randomNumber + it.contentCategoryTaxonomy = PBSUtils.randomNumber + it.contentTitle = PBSUtils.randomString + it.contentUrl = PBSUtils.randomString + it.contentLivestream = PBSUtils.randomNumber + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest(distributionType) + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain contentGenre from param" + verifyAll(getRequestContent(bidder.getBidderRequest(request.id))) { + it.genre == generalGetRequest.contentGenre + it.language == generalGetRequest.contentLanguage + it.contentrating == generalGetRequest.contentRating + it.cat == [generalGetRequest.contentCategory.toString()] + it.cattax == generalGetRequest.contentCategoryTaxonomy + it.title == generalGetRequest.contentTitle + it.url == generalGetRequest.contentUrl + it.livestream == generalGetRequest.contentLivestream + } + + where: + distributionType << [SITE, APP, DOOH] + } + + def "PBS should apply series from general get request when it's specified"() { + given: "Default General get request" + def contentSeries = PBSUtils.randomString + def generalGetRequest = (rawGeneralGetRequest(contentSeries) as GeneralGetRequest).tap { + it.storedRequestId = PBSUtils.randomString + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest(distributionType) + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain contentSeries from param" + assert getRequestContent(bidder.getBidderRequest(request.id)).series == contentSeries + + where: + distributionType | rawGeneralGetRequest + SITE | { String series -> new GeneralGetRequest(contentSeries: series) } + APP | { String series -> new GeneralGetRequest(contentSeries: series) } + DOOH | { String series -> new GeneralGetRequest(contentSeries: series) } + + SITE | { String series -> new GeneralGetRequest(contentSeriesAlias: series) } + APP | { String series -> new GeneralGetRequest(contentSeriesAlias: series) } + DOOH | { String series -> new GeneralGetRequest(contentSeriesAlias: series) } + + SITE | { String series -> new GeneralGetRequest(contentSeries: series, contentSeriesAlias: PBSUtils.randomString) } + APP | { String series -> new GeneralGetRequest(contentSeries: series, contentSeriesAlias: PBSUtils.randomString) } + DOOH | { String series -> new GeneralGetRequest(contentSeries: series, contentSeriesAlias: PBSUtils.randomString) } + } + + def "PBS should apply device ip info from general get request when it's specified"() { + given: "Default General get request" + def generalGetRequest = GeneralGetRequest.default.tap { + it.deviceIp = deviceIp + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain device ip from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert getDeviceIp(bidderRequest.device) == deviceIp + + where: + deviceIp << [ + PBSUtils.getRandomEnum(PublicCountryIp).v4, + PBSUtils.getRandomEnum(PublicCountryIp).maskedIPv6 + ] + } + + def "PBS should apply device params from general get request when it's specified"() { + given: "Default General get request" + def generalGetRequest = GeneralGetRequest.default.tap { + it.deviceUa = PBSUtils.randomString + it.deviceType = PBSUtils.getRandomEnum(DeviceType) + it.deviceIfa = PBSUtils.randomString + it.deviceIfaType = PBSUtils.randomString + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain device info from param" + def bidderRequest = bidder.getBidderRequest(request.id) + verifyAll(bidderRequest.device) { + it.ua == generalGetRequest.deviceUa + it.devicetype == generalGetRequest.deviceType + it.ifa == generalGetRequest.deviceIfa + it.ext.ifaType == generalGetRequest.deviceIfaType + } + } + + def "PBS should apply site page info from header of get request when it's specified"() { + given: "Default General get request" + def generalGetRequest = GeneralGetRequest.getDefault() + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + site.page = null + site.publisher.domain = PBSUtils.randomString + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def page = "http://${PBSUtils.randomString}" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest, ['Referer': page]) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain site page from header" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.site.page == page + } + + def "PBS should apply user agent info from #header header of get request when parameter is not specified"() { + given: "Default General get request" + def generalGetRequest = GeneralGetRequest.getDefault() + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def ua = PBSUtils.randomString + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest, ['User-Agent': '', (header): ua]) // remove original user-agent + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain device ua from header" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.device.ua == ua + + where: + header << ["User-Agent", "X-Device-User-Agent"] + } + + def "PBS should apply device ip info from #header header of get request when parameter is not specified"() { + given: "Default General get request" + def generalGetRequest = GeneralGetRequest.getDefault() + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest, [(header): deviceIp]) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain device ip from headers" + def bidderRequest = bidder.getBidderRequest(request.id) + assert getDeviceIp(bidderRequest.device) == deviceIp + + where: + header | deviceIp + "X-Forwarded-For" | PBSUtils.getRandomEnum(PublicCountryIp).v4 + "X-Forwarded-For" | PBSUtils.getRandomEnum(PublicCountryIp).maskedIPv6 + + "X-Device-IP" | PBSUtils.getRandomEnum(PublicCountryIp).v4 + "X-Device-IP" | PBSUtils.getRandomEnum(PublicCountryIp).maskedIPv6 + + "X-Real-IP" | PBSUtils.getRandomEnum(PublicCountryIp).v4 + "X-Real-IP" | PBSUtils.getRandomEnum(PublicCountryIp).maskedIPv6 + + "True-Client-IP" | PBSUtils.getRandomEnum(PublicCountryIp).v4 + "True-Client-IP" | PBSUtils.getRandomEnum(PublicCountryIp).maskedIPv6 + } + + def "PBS should use original values from stored request when it's not specified in get request"() { + given: "Default General get request" + def storedRequestId = PBSUtils.randomString + def generalGetRequest = new GeneralGetRequest(storedRequestId: storedRequestId) + + and: "Default stored request" + def bidRequestDevice = new Device().tap { + it.dnt = PBSUtils.randomNumber + it.lmt = PBSUtils.randomNumber + it.ip = PBSUtils.getRandomEnum(PublicCountryIp).v4 + it.ipv6 = PBSUtils.getRandomEnum(PublicCountryIp).maskedIPv6 + it.ua = PBSUtils.randomString + it.devicetype = PBSUtils.getRandomEnum(DeviceType) + it.ifa = PBSUtils.randomString + it.ext = new DeviceExt(ifaType: PBSUtils.randomString) + } + + def request = BidRequest.getDefaultBidRequest().tap { + it.tmax = PBSUtils.getRandomNumber(1000, 5000) + it.bcat = [PBSUtils.randomString] + it.badv = [PBSUtils.randomString] + it.device = bidRequestDevice + + it.ext.prebid.debug = PBSUtils.getRandomEnum(DebugCondition) + it.ext.prebid.outputFormat = PBSUtils.randomString + it.ext.prebid.outputModule = PBSUtils.randomString + it.ext.prebid.storedAuctionResponse = new StoredAuctionResponse(id: PBSUtils.randomNumber) + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(storedRequestId, request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain device ip from headers" + verifyAll(bidder.getBidderRequest(request.id)) { + it.bcat == request.bcat + it.badv == request.badv + it.device == bidRequestDevice + + it.ext.prebid.debug == request.ext.prebid.debug + it.ext.prebid.outputFormat == request.ext.prebid.outputFormat + it.ext.prebid.outputModule == request.ext.prebid.outputModule + it.ext.prebid.storedAuctionResponse == request.ext.prebid.storedAuctionResponse + } + } + + def "PBS should use original content values from stored #channel request when it's not specified in get request"() { + given: "Default General get request" + def storedRequestId = PBSUtils.randomString + def generalGetRequest = new GeneralGetRequest(storedRequestId: storedRequestId) + + and: "Default stored request" + def content = Content.getDefaultContent().tap { + it.genre = PBSUtils.randomString + it.language = PBSUtils.randomString + it.contentrating = PBSUtils.randomString + it.cat = [PBSUtils.randomString] + it.cattax = PBSUtils.randomNumber + it.series = PBSUtils.randomString + it.title = PBSUtils.randomString + it.url = PBSUtils.randomString + it.livestream = PBSUtils.randomNumber + } + def request = BidRequest.getDefaultBidRequest(channel).tap { + it.getProperty(channel.value).content = content + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(storedRequestId, request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = defaultPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain content data from headers" + assert getRequestContent(bidder.getBidderRequest(request.id)) == content + + where: + channel << [SITE, APP, DOOH] + } + + static String getDeviceIp(Device device) { + device.ip ?: device.ipv6 + } + + static Content getRequestContent(BidderRequest bidderRequest) { + def distributionChannels = bidderRequest.getRequestDistributionChannels() + + if (distributionChannels.contains(SITE)) { + return bidderRequest.site.content + } + if (distributionChannels.contains(APP)) { + return bidderRequest.app.content + } + return bidderRequest.dooh.content + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/ProfileSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/ProfileSpec.groovy index c6d600d518c..6ae3c70c29b 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/ProfileSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/ProfileSpec.groovy @@ -19,6 +19,7 @@ import org.prebid.server.functional.model.request.auction.ImpExt import org.prebid.server.functional.model.request.auction.ImpExtPrebid import org.prebid.server.functional.model.request.auction.StoredAuctionResponse import org.prebid.server.functional.model.request.auction.StoredBidResponse +import org.prebid.server.functional.model.request.get.GeneralGetRequest import org.prebid.server.functional.model.request.profile.Profile import org.prebid.server.functional.model.request.profile.ImpProfile import org.prebid.server.functional.model.request.profile.ProfileMergePrecedence @@ -1518,7 +1519,7 @@ class ProfileSpec extends BaseSpec { ] } - def "PBS should throw exception when profiles are not configured for filesystem and request contain profileId"() { + def "PBS should process request with warning when profiles are not configured for filesystem and request contain profileId"() { given: "PBS with profiles.fail-on-unknown config" def config = FILESYSTEM_CONFIG + PROFILES_CONFIG + ['settings.filesystem.profiles-dir': null] pbsContainer = new PrebidServerContainer(config) @@ -1526,34 +1527,601 @@ class ProfileSpec extends BaseSpec { pbsContainer.withFolder(IMPS_PATH) pbsContainer.withFolder(RESPONSES_PATH) pbsContainer.withFolder(CATEGORIES_PATH) + pbsContainer.withCopyToContainer(Transferable.of(encode(fileRequestProfileWithEmptyMerge)), "$PROFILES_PATH/${fileRequestProfileWithEmptyMerge.fileName}") def accountsConfig = new FileSystemAccountsConfig(accounts: [new AccountConfig(id: ACCOUNT_ID_FILE_STORAGE, status: ACTIVE)]) pbsContainer.withCopyToContainer(Transferable.of(encodeYaml(accountsConfig)), SETTINGS_FILENAME) pbsContainer.start() - pbsWithStoredProfiles = new PrebidServerService(pbsContainer) + def pbsWithStoredProfiles = new PrebidServerService(pbsContainer) and: "BidRequest with profile" - def requestWithProfile = BidRequest.getDefaultBidRequest().tap { - it.ext.prebid.profileNames = [PBSUtils.randomString] - } + def requestWithProfile = getRequestWithProfiles(ACCOUNT_ID_FILE_STORAGE.toString(), [fileRequestProfileWithEmptyMerge]).tap { + it.device = Device.default + } as BidRequest when: "PBS processes auction request" - defaultPbsService.sendAuctionRequest(requestWithProfile) + def response = pbsWithStoredProfiles.sendAuctionRequest(requestWithProfile) - then: "PBs should throw error due to invalid profile config" - def exception = thrown(PrebidServerException) - assert exception.statusCode == 400 - assert exception.responseBody == INVALID_REQUEST_PREFIX + CONFIG_ERROR_MESSAGE + then: "PBS should emit proper warning" + assert response.ext?.warnings[ErrorType.PREBID]*.code == [999] + assert response.ext?.warnings[ErrorType.PREBID]*.message == [NO_REQUEST_PROFILE_MESSAGE.formatted(fileRequestProfileWithEmptyMerge.id)] + + and: "Response should contain error" + assert !response.ext?.errors + + and: "Missing metric should increments" + def metrics = pbsWithStoredProfiles.sendCollectedMetricsRequest() + assert metrics[MISSING_ACCOUNT_PROFILE_METRIC.formatted(ACCOUNT_ID_FILE_STORAGE)] == 1 + + and: "Bidder request should contain data from profile" + verifyAll(bidder.getBidderRequest(requestWithProfile.id)) { + it.site.id == requestWithProfile.site.id + it.site.name == requestWithProfile.site.name + it.site.domain == requestWithProfile.site.domain + it.site.cat == requestWithProfile.site.cat + it.site.sectionCat == requestWithProfile.site.sectionCat + it.site.pageCat == requestWithProfile.site.pageCat + it.site.page == requestWithProfile.site.page + it.site.ref == requestWithProfile.site.ref + it.site.search == requestWithProfile.site.search + it.site.keywords == requestWithProfile.site.keywords + + it.device.didsha1 == requestWithProfile.device.didsha1 + it.device.didmd5 == requestWithProfile.device.didmd5 + it.device.dpidsha1 == requestWithProfile.device.dpidsha1 + it.device.ifa == requestWithProfile.device.ifa + it.device.macsha1 == requestWithProfile.device.macsha1 + it.device.macmd5 == requestWithProfile.device.macmd5 + it.device.dpidmd5 == requestWithProfile.device.dpidmd5 + } cleanup: "Stop and remove pbs container" pbsContainer.stop() } + def "PBS should use request profile for general get request when profile included as parameter"() { + given: "Default profile in database" + def accountId = PBSUtils.randomNumber as String + def requestProfile = RequestProfile.getProfile(accountId) + profileRequestDao.save(StoredProfileRequest.getProfile(requestProfile)) + + and: "Default get request" + def getRequest = GeneralGetRequest.getDefault().tap { + it.requestProfiles = [requestProfile.id] + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.device = Device.default + setAccountId(accountId) + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(getRequest.storedRequestId, request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = pbsWithStoredProfiles.sendGeneralGetRequest(getRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain data from profile" + verifyAll(bidder.getBidderRequest(request.id)) { + it.site.id == requestProfile.body.site.id + it.site.name == requestProfile.body.site.name + it.site.domain == requestProfile.body.site.domain + it.site.cat == requestProfile.body.site.cat + it.site.sectionCat == requestProfile.body.site.sectionCat + it.site.pageCat == requestProfile.body.site.pageCat + it.site.page == requestProfile.body.site.page + it.site.ref == requestProfile.body.site.ref + it.site.search == requestProfile.body.site.search + it.site.keywords == requestProfile.body.site.keywords + + it.device.didsha1 == requestProfile.body.device.didsha1 + it.device.didmd5 == requestProfile.body.device.didmd5 + it.device.dpidsha1 == requestProfile.body.device.dpidsha1 + it.device.ifa == requestProfile.body.device.ifa + it.device.macsha1 == requestProfile.body.device.macsha1 + it.device.macmd5 == requestProfile.body.device.macmd5 + it.device.dpidmd5 == requestProfile.body.device.dpidmd5 + } + } + + def "PBS should use imp profile for general get request when profile included as parameter"() { + given: "Default profile in database" + def accountId = PBSUtils.randomNumber as String + def impProfile = ImpProfile.getProfile(accountId) + profileImpDao.save(StoredProfileImp.getProfile(impProfile)) + + and: "Default get request" + def getRequest = GeneralGetRequest.getDefault().tap { + it.impProfiles = [impProfile.id] + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + setAccountId(accountId) + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(getRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = pbsWithStoredProfiles.sendGeneralGetRequest(getRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request imp should contain data from profile" + verifyAll(bidder.getBidderRequest(request.id).imp) { + it.id == [impProfile.body.id] + it.banner == [impProfile.body.banner] + } + } + + def "PBS should use request profile for general get request when profile included as parameter and exist in filesystem"() { + given: "Default get request" + def generalGetRequest = GeneralGetRequest.default.tap { + it.requestProfiles = [fileRequestProfile.id] + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + setAccountId(ACCOUNT_ID_FILE_STORAGE.toString()) + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = pbsWithStoredProfiles.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain data from profile" + verifyAll(bidder.getBidderRequest(request.id)) { + it.site.id == fileRequestProfile.body.site.id + it.site.name == fileRequestProfile.body.site.name + it.site.domain == fileRequestProfile.body.site.domain + it.site.cat == fileRequestProfile.body.site.cat + it.site.sectionCat == fileRequestProfile.body.site.sectionCat + it.site.pageCat == fileRequestProfile.body.site.pageCat + it.site.page == fileRequestProfile.body.site.page + it.site.ref == fileRequestProfile.body.site.ref + it.site.search == fileRequestProfile.body.site.search + it.site.keywords == fileRequestProfile.body.site.keywords + it.site.ext.data == fileRequestProfile.body.site.ext.data + + it.device.didsha1 == fileRequestProfile.body.device.didsha1 + it.device.didmd5 == fileRequestProfile.body.device.didmd5 + it.device.dpidsha1 == fileRequestProfile.body.device.dpidsha1 + it.device.ifa == fileRequestProfile.body.device.ifa + it.device.macsha1 == fileRequestProfile.body.device.macsha1 + it.device.macmd5 == fileRequestProfile.body.device.macmd5 + it.device.dpidmd5 == fileRequestProfile.body.device.dpidmd5 + } + } + + def "PBS should use imp profile for general get request when profile included as parameter and exist in filesystem"() { + given: "Default GeneralGetRequest" + def generalGetRequest = GeneralGetRequest.default.tap { + it.impProfiles = [fileImpProfile.id] + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + setAccountId(ACCOUNT_ID_FILE_STORAGE.toString()) + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = pbsWithStoredProfiles.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request imp should contain data from profile" + verifyAll(bidder.getBidderRequest(request.id).imp) { + it.id == [fileImpProfile.body.id] + it.banner == [fileImpProfile.body.banner] + } + } + + def "PBS should emit error and metrics when request profile called from imp level for profile general get parameter"() { + given: "Default profile in database" + def accountId = PBSUtils.randomNumber as String + def requestProfile = RequestProfile.getProfile(accountId) + profileRequestDao.save(StoredProfileRequest.getProfile(requestProfile)) + + and: "Default GeneralGetRequest" + def generalGetRequest = GeneralGetRequest.default.tap { + it.impProfiles = [requestProfile.id] + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + it.device = Device.default + setAccountId(accountId) + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = pbsWithStoredProfiles.sendGeneralGetRequest(generalGetRequest) + + then: "PBS should emit proper warning" + assert response.ext?.warnings[ErrorType.PREBID]*.code == [999] + assert response.ext?.warnings[ErrorType.PREBID]*.message == [NO_PROFILE_MESSAGE.formatted(requestProfile.id)] + + and: "Response should contain error" + assert !response.ext?.errors + + and: "Missing metric should increments" + def metrics = pbsWithStoredProfiles.sendCollectedMetricsRequest() + assert metrics[MISSING_ACCOUNT_PROFILE_METRIC.formatted(accountId)] == 1 + + and: "Bidder request should contain data from profile" + verifyAll(bidder.getBidderRequest(request.id)) { + it.site.id == request.site.id + it.site.name == request.site.name + it.site.domain == request.site.domain + it.site.cat == request.site.cat + it.site.sectionCat == request.site.sectionCat + it.site.pageCat == request.site.pageCat + it.site.page == request.site.page + it.site.ref == request.site.ref + it.site.search == request.site.search + it.site.keywords == request.site.keywords + + it.device.didsha1 == request.device.didsha1 + it.device.didmd5 == request.device.didmd5 + it.device.dpidsha1 == request.device.dpidsha1 + it.device.ifa == request.device.ifa + it.device.macsha1 == request.device.macsha1 + it.device.macmd5 == request.device.macmd5 + it.device.dpidmd5 == request.device.dpidmd5 + } + } + + def "PBS should emit error and metrics when imp profile called from request level for profile general get parameter"() { + given: "Default profile in database" + def accountId = PBSUtils.randomNumber as String + def impProfile = ImpProfile.getProfile(accountId) + profileImpDao.save(StoredProfileImp.getProfile(impProfile)) + + and: "Default GeneralGetRequest" + def generalGetRequest = GeneralGetRequest.default.tap { + it.requestProfiles = [impProfile.id] + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + setAccountId(accountId) + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = pbsWithStoredProfiles.sendGeneralGetRequest(generalGetRequest) + + then: "PBS should emit proper warning" + assert response.ext?.warnings[ErrorType.PREBID]*.code == [999] + assert response.ext?.warnings[ErrorType.PREBID]*.message == [NO_PROFILE_MESSAGE.formatted(impProfile.id)] + + and: "Response should contain error" + assert !response.ext?.errors + + and: "Missing metric should increments" + def metrics = pbsWithStoredProfiles.sendCollectedMetricsRequest() + assert metrics[MISSING_ACCOUNT_PROFILE_METRIC.formatted(accountId)] == 1 + + and: "Bidder request imp should contain data from original imp" + assert bidder.getBidderRequest(request.id).imp.banner == request.imp.banner + } + + def "PBS should emit error and metrics when request profile missing for profile general get parameter"() { + given: "Default GeneralGetRequest" + def requestProfile = PBSUtils.randomString + def generalGetRequest = GeneralGetRequest.default.tap { + it.requestProfiles = [requestProfile] + } + + and: "Default stored request" + def accountId = PBSUtils.randomNumber as String + def request = BidRequest.getDefaultBidRequest().tap { + it.device = Device.default + setAccountId(accountId) + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.storedRequestId, request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = pbsWithStoredProfiles.sendGeneralGetRequest(generalGetRequest) + + then: "PBS should emit proper warning" + assert response.ext?.warnings[ErrorType.PREBID]*.code == [999] + assert response.ext?.warnings[ErrorType.PREBID]*.message == [NO_REQUEST_PROFILE_MESSAGE.formatted(requestProfile)] + + and: "Response should contain error" + assert !response.ext?.errors + + and: "Missing metric should increments" + def metrics = pbsWithStoredProfiles.sendCollectedMetricsRequest() + assert metrics[MISSING_ACCOUNT_PROFILE_METRIC.formatted(accountId)] == 1 + + and: "Bidder request should contain data from original stored request" + verifyAll(bidder.getBidderRequest(request.id)) { + it.site.id == request.site.id + it.site.name == request.site.name + it.site.domain == request.site.domain + it.site.cat == request.site.cat + it.site.sectionCat == request.site.sectionCat + it.site.pageCat == request.site.pageCat + it.site.page == request.site.page + it.site.ref == request.site.ref + it.site.search == request.site.search + it.site.keywords == request.site.keywords + + it.device.didsha1 == request.device.didsha1 + it.device.didmd5 == request.device.didmd5 + it.device.dpidsha1 == request.device.dpidsha1 + it.device.ifa == request.device.ifa + it.device.macsha1 == request.device.macsha1 + it.device.macmd5 == request.device.macmd5 + it.device.dpidmd5 == request.device.dpidmd5 + } + + and: "Bidder request imp should contain data from original imp" + assert bidder.getBidderRequest(request.id).imp.banner == request.imp.banner + + where: + requestParam | impParam + [PBSUtils.randomString] | null + null | [PBSUtils.randomString] + } + + def "PBS should emit error and metrics when imp profile missing for profile general get parameter"() { + given: "Default GeneralGetRequest" + def impProfileId = PBSUtils.randomString + def generalGetRequest = GeneralGetRequest.default.tap { + it.impProfiles = [impProfileId] + } + + and: "Default stored request" + def accountId = PBSUtils.randomNumber as String + def request = BidRequest.getDefaultBidRequest().tap { + it.device = Device.default + setAccountId(accountId) + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = pbsWithStoredProfiles.sendGeneralGetRequest(generalGetRequest) + + then: "PBS should emit proper warning" + assert response.ext?.warnings[ErrorType.PREBID]*.code == [999] + assert response.ext?.warnings[ErrorType.PREBID]*.message == [NO_IMP_PROFILE_MESSAGE.formatted(impProfileId)] + + and: "Response should contain error" + assert !response.ext?.errors + + and: "Missing metric should increments" + def metrics = pbsWithStoredProfiles.sendCollectedMetricsRequest() + assert metrics[MISSING_ACCOUNT_PROFILE_METRIC.formatted(accountId)] == 1 + + and: "Bidder request should contain data from original stored request" + verifyAll(bidder.getBidderRequest(request.id)) { + it.site.id == request.site.id + it.site.name == request.site.name + it.site.domain == request.site.domain + it.site.cat == request.site.cat + it.site.sectionCat == request.site.sectionCat + it.site.pageCat == request.site.pageCat + it.site.page == request.site.page + it.site.ref == request.site.ref + it.site.search == request.site.search + it.site.keywords == request.site.keywords + + it.device.didsha1 == request.device.didsha1 + it.device.didmd5 == request.device.didmd5 + it.device.dpidsha1 == request.device.dpidsha1 + it.device.ifa == request.device.ifa + it.device.macsha1 == request.device.macsha1 + it.device.macmd5 == request.device.macmd5 + it.device.dpidmd5 == request.device.dpidmd5 + } + + and: "Bidder request imp should contain data from original imp" + assert bidder.getBidderRequest(request.id).imp.banner == request.imp.banner + } + + def "PBS should prioritise profile for request and emit warning when profile included as parameter more than limit"() { + given: "Default profiles in database" + def accountId = PBSUtils.randomNumber as String + def profileSite = Site.rootFPDSite + def profileDevice = Device.default + def firstRequestProfile = RequestProfile.getProfile(accountId).tap { + it.body.site = profileSite + it.body.device = null + } + def secondRequestProfile = RequestProfile.getProfile(accountId).tap { + it.body.site = null + it.body.device = profileDevice + } + def impProfile = ImpProfile.getProfile(accountId, Imp.getDefaultImpression(VIDEO)) + profileRequestDao.save(StoredProfileRequest.getProfile(firstRequestProfile)) + profileRequestDao.save(StoredProfileRequest.getProfile(secondRequestProfile)) + profileImpDao.save(StoredProfileImp.getProfile(impProfile)) + + and: "Default GeneralGetRequest" + def generalGetRequest = GeneralGetRequest.default.tap { + it.requestProfiles = [firstRequestProfile, secondRequestProfile].id + it.impProfiles = [impProfile.id] + } + + and: "Default stored request" + def bidRequest = BidRequest.getDefaultBidRequest().tap { + setAccountId(accountId) + } + + and: "Flash metrics" + flushMetrics(pbsWithStoredProfiles) + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), bidRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = pbsWithStoredProfiles.sendGeneralGetRequest(generalGetRequest) + + then: "Response should contain error" + assert !response.ext?.errors + + and: "Missing metric should increments" + def metrics = pbsWithStoredProfiles.sendCollectedMetricsRequest() + assert metrics[LIMIT_EXCEEDED_ACCOUNT_PROFILE_METRIC.formatted(accountId)] == 1 + + and: "Bidder request should contain data from profile" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + verifyAll(bidderRequest) { + it.site.id == profileSite.id + it.site.name == profileSite.name + it.site.domain == profileSite.domain + it.site.cat == profileSite.cat + it.site.sectionCat == profileSite.sectionCat + it.site.pageCat == profileSite.pageCat + it.site.page == profileSite.page + it.site.ref == profileSite.ref + it.site.search == profileSite.search + it.site.keywords == profileSite.keywords + it.site.ext.data == profileSite.ext.data + + it.device.didsha1 == profileDevice.didsha1 + it.device.didmd5 == profileDevice.didmd5 + it.device.dpidsha1 == profileDevice.dpidsha1 + it.device.ifa == profileDevice.ifa + it.device.macsha1 == profileDevice.macsha1 + it.device.macmd5 == profileDevice.macmd5 + it.device.dpidmd5 == profileDevice.dpidmd5 + } + + and: "Bidder imp should contain original data from request" + assert verifyAll(bidderRequest.imp) { + it.banner == bidRequest.imp.banner + it.video == [null] + } + } + + def "PBS should override request profile from stored general get request when profile included as parameter"() { + given: "Default profile in database" + def accountId = PBSUtils.randomNumber as String + def requestProfile = RequestProfile.getProfile(accountId) + profileRequestDao.save(StoredProfileRequest.getProfile(requestProfile)) + + and: "Default GeneralGetRequest" + def generalGetRequest = GeneralGetRequest.default.tap { + it.requestProfiles = [requestProfile.id] + } + + and: "Stored request with profile" + def request = BidRequest.getDefaultBidRequest().tap { + it.ext.prebid.profileNames = [PBSUtils.randomString] + setAccountId(accountId) + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.storedRequestId, request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = pbsWithStoredProfiles.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain data from profile" + verifyAll(bidder.getBidderRequest(request.id)) { + it.site.id == requestProfile.body.site.id + it.site.name == requestProfile.body.site.name + it.site.domain == requestProfile.body.site.domain + it.site.cat == requestProfile.body.site.cat + it.site.sectionCat == requestProfile.body.site.sectionCat + it.site.pageCat == requestProfile.body.site.pageCat + it.site.page == requestProfile.body.site.page + it.site.ref == requestProfile.body.site.ref + it.site.search == requestProfile.body.site.search + it.site.keywords == requestProfile.body.site.keywords + it.site.ext.data == requestProfile.body.site.ext.data + + it.device.didsha1 == requestProfile.body.device.didsha1 + it.device.didmd5 == requestProfile.body.device.didmd5 + it.device.dpidsha1 == requestProfile.body.device.dpidsha1 + it.device.ifa == requestProfile.body.device.ifa + it.device.macsha1 == requestProfile.body.device.macsha1 + it.device.macmd5 == requestProfile.body.device.macmd5 + it.device.dpidmd5 == requestProfile.body.device.dpidmd5 + } + } + + def "PBS should override imp profile from stored general get request when profile included as parameter"() { + given: "Default profile in database" + def accountId = PBSUtils.randomNumber as String + def impProfile = ImpProfile.getProfile(accountId) + profileImpDao.save(StoredProfileImp.getProfile(impProfile)) + + and: "Default GeneralGetRequest" + def generalGetRequest = GeneralGetRequest.default.tap { + it.impProfiles = [impProfile.id] + } + + and: "Stored request with profile" + def request = BidRequest.getDefaultBidRequest().tap { + it.imp.first.ext.prebid.profileNames = [PBSUtils.randomString] + setAccountId(accountId) + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = pbsWithStoredProfiles.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request imp should contain data from profile" + verifyAll(bidder.getBidderRequest(request.id).imp) { + it.id == [impProfile.body.id] + it.banner == [impProfile.body.banner] + } + } + private static BidRequest getRequestWithProfiles(String accountId, List profiles) { BidRequest.getDefaultBidRequest().tap { - if (profiles.type.contains(ProfileType.IMP)) { - it.imp.first.ext.prebid.profileNames = profiles.findAll { it.type == ProfileType.IMP }*.id - } it.imp.first.ext.prebid.profileNames = profiles.findAll { it.type == ProfileType.IMP }*.id it.ext.prebid.profileNames = profiles.findAll { it.type == ProfileType.REQUEST }*.id setAccountId(accountId) diff --git a/src/test/groovy/org/prebid/server/functional/tests/TargetingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/TargetingSpec.groovy index d9d337b3c27..a7fb7db0b84 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/TargetingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/TargetingSpec.groovy @@ -1107,7 +1107,12 @@ class TargetingSpec extends BaseSpec { def "PBS amp should apply data from query to ext.prebid.amp.data"() { given: "Default AmpRequest" - def ampRequest = AmpRequest.defaultAmpRequest + def unknownValue = PBSUtils.randomString + def secondUnknownValue = PBSUtils.randomNumber + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.unknownField = unknownValue + it.secondUnknownField = secondUnknownValue + } and: "Bid request" def ampStoredRequest = BidRequest.defaultBidRequest @@ -1117,10 +1122,7 @@ class TargetingSpec extends BaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp request" - def unknownValue = PBSUtils.randomString - def secondUnknownValue = PBSUtils.randomNumber - pbsWithDefaultTargetingLength.sendAmpRequestWithAdditionalQueries(ampRequest, ["unknown_field" : unknownValue, - "second_unknown_field": secondUnknownValue]) + pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp should contain data from query request" def bidderRequests = bidder.getBidderRequest(ampStoredRequest.id) diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GeneralGetInterfacePrivacySpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/GeneralGetInterfacePrivacySpec.groovy new file mode 100644 index 00000000000..c9ebf7a2ac1 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/GeneralGetInterfacePrivacySpec.groovy @@ -0,0 +1,554 @@ +package org.prebid.server.functional.tests.privacy + +import org.prebid.server.functional.model.config.AccountGdprConfig +import org.prebid.server.functional.model.config.PurposeConfig +import org.prebid.server.functional.model.db.StoredRequest +import org.prebid.server.functional.model.request.GppSectionId +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.ConsentedProvidersSettings +import org.prebid.server.functional.model.request.auction.DebugCondition +import org.prebid.server.functional.model.request.auction.Regs +import org.prebid.server.functional.model.request.auction.RegsExt +import org.prebid.server.functional.model.request.auction.User +import org.prebid.server.functional.model.request.auction.UserExt +import org.prebid.server.functional.model.request.get.GeneralGetRequest +import org.prebid.server.functional.util.PBSUtils +import org.prebid.server.functional.util.privacy.CcpaConsent +import org.prebid.server.functional.util.privacy.TcfConsent +import org.prebid.server.functional.util.privacy.gpp.UsNatV1Consent +import spock.lang.PendingFeature + +import static org.prebid.server.functional.model.config.Purpose.P2 +import static org.prebid.server.functional.model.config.PurposeEnforcement.BASIC +import static org.prebid.server.functional.model.config.PurposeEnforcement.NO +import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ADAPTER_DISALLOWED_COUNT +import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_REQUEST_DISALLOWED_COUNT +import static org.prebid.server.functional.model.request.GppSectionId.HEADER_V1 +import static org.prebid.server.functional.model.request.GppSectionId.TCF_EU_V2 +import static org.prebid.server.functional.model.request.amp.ConsentType.BOGUS +import static org.prebid.server.functional.model.request.amp.ConsentType.GPP +import static org.prebid.server.functional.model.request.amp.ConsentType.TCF_1 +import static org.prebid.server.functional.model.request.amp.ConsentType.TCF_2 +import static org.prebid.server.functional.model.request.amp.ConsentType.US_PRIVACY +import static org.prebid.server.functional.model.request.auction.ActivityType.FETCH_BIDS +import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID +import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID +import static org.prebid.server.functional.util.privacy.TcfConsent.PurposeId.BASIC_ADS + +class GeneralGetInterfacePrivacySpec extends PrivacyBaseSpec { + + def "PBS should apply gpp consent from general get request when it's specified"() { + given: "Default General get request" + def consentValue = new TcfConsent.Builder() + .setPurposesLITransparency(BASIC_ADS) + .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .build().toString() + def generalGetRequest = (consentGeneralRequest(consentValue) as GeneralGetRequest).tap { + it.storedRequestId = PBSUtils.randomNumber + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = privacyPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain user consent info from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.user.consent == consentValue + + where: + consentGeneralRequest << + [ + { String tcfConsent -> new GeneralGetRequest(tcfConsent: tcfConsent) }, + { String tcfConsent -> new GeneralGetRequest(tcfConsent: tcfConsent, generalConsent: PBSUtils.randomString) }, + { String tcfConsent -> new GeneralGetRequest(tcfConsent: tcfConsent, generalConsentString: PBSUtils.randomString) }, + ] + } + + def "PBS should recognise consent from general get request as tcfv1 when consent type is tcf1"() { + given: "Default General get request" + def consentValue = new TcfConsent.Builder() + .setPurposesLITransparency(BASIC_ADS) + .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .build().toString() + def generalGetRequest = (consentGeneralRequest(consentValue) as GeneralGetRequest).tap { + it.storedRequestId = PBSUtils.randomNumber + it.consentType = TCF_1 + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = privacyPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain user consent info from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.user.consent == consentValue + + where: + consentGeneralRequest << + [ + { String gppConsent -> new GeneralGetRequest(generalConsent: gppConsent) }, + { String gppConsent -> new GeneralGetRequest(generalConsentString: gppConsent) }, + { String gppConsent -> new GeneralGetRequest(generalConsentString: gppConsent, generalConsent: PBSUtils.randomString) } + ] + } + + def "PBS should recognise consent from general get request as us_privacy when consent type is us_privacy"() { + given: "Default General get request" + def generalGetRequest = (consentGeneralRequest(new CcpaConsent().getConsentString()) as GeneralGetRequest).tap { + it.storedRequestId = PBSUtils.randomNumber + it.consentType = US_PRIVACY + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = privacyPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Generic bidderRequest should contain us privacy info" + def bidderRequest = bidder.getBidderRequest(request.id) + verifyAll (bidderRequest) { + regs.usPrivacy == new CcpaConsent().getConsentString() + regs.ext == new RegsExt() + } + + and: "Shouldn't contain other privacy info" + verifyAll (bidderRequest.regs) { + !it.coppa + !it.gpc + !it.gpp + !it.gppSid + } + + where: + consentGeneralRequest << + [ + { String gppConsent -> new GeneralGetRequest(generalConsent: gppConsent) }, + { String gppConsent -> new GeneralGetRequest(generalConsentString: gppConsent) }, + { String gppConsent -> new GeneralGetRequest(generalConsentString: gppConsent, generalConsent: PBSUtils.randomString) } + ] + } + + def "PBS should recognise consent from general get request as gpp when consent type is gpp"() { + given: "Default General get request" + def consentValue = new UsNatV1Consent.Builder().build().toString() + def generalGetRequest = (consentGeneralRequest(consentValue) as GeneralGetRequest).tap { + it.storedRequestId = PBSUtils.randomNumber + it.consentType = GPP + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = privacyPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Generic bidderRequest should contain gpp info" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.regs.gpp == consentValue + + + and: "Shouldn't contain other privacy info" + verifyAll (bidderRequest.regs) { + !it.coppa + !it.gpc + !it.gppSid + } + + and: "Shouldn't contain ext privacy info" + assert bidderRequest.regs.ext == new RegsExt() + + where: + consentGeneralRequest << + [ + { String gppConsent -> new GeneralGetRequest(generalConsent: gppConsent) }, + { String gppConsent -> new GeneralGetRequest(generalConsentString: gppConsent) }, + { String gppConsent -> new GeneralGetRequest(generalConsentString: gppConsent, generalConsent: PBSUtils.randomString) } + ] + } + + @PendingFeature + def "PBS should emit error when consent type is invalid"() { + given: "Default General get request" + def consentValue = PBSUtils.randomString + def accountId = PBSUtils.randomNumber.toString() + def generalGetRequest = (consentGeneralRequest(consentValue) as GeneralGetRequest).tap { + it.storedRequestId = PBSUtils.randomNumber + it.accountId = accountId + it.consentType = BOGUS + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + setAccountId(accountId) + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = privacyPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should contain error" + assert response.ext?.errors[PREBID]*.code == [999] + assert response.ext?.errors[PREBID]*.message == ["Invalid consent_type param passed"] + + where: + consentGeneralRequest << + [ + { String gppConsent -> new GeneralGetRequest(generalConsent: gppConsent) }, + { String gppConsent -> new GeneralGetRequest(generalConsentString: gppConsent) }, + { String gppConsent -> new GeneralGetRequest(generalConsentString: gppConsent, generalConsent: PBSUtils.randomString) } + ] + } + + def "PBS should apply gdpr from general get request when it's specified"() { + given: "Default General get request" + def gdprValue = PBSUtils.randomBinary + def accountId = PBSUtils.randomNumber + def generalGetRequest = (consentGeneralRequest(gdprValue) as GeneralGetRequest).tap { + it.storedRequestId = PBSUtils.randomNumber + it.accountId = accountId + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + setAccountId(accountId as String) + } + + and: "Save account config with requireConsent into DB" + def purposes = [(P2): new PurposeConfig(enforcePurpose: NO, enforceVendors: false)] + def accountGdprConfig = new AccountGdprConfig(purposes: purposes) + def account = getAccountWithGdpr(accountId as String, accountGdprConfig) + accountDao.save(account) + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = privacyPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain user consent info from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.regs.gdpr == gdprValue + + where: + consentGeneralRequest << [ + { Integer gdpr -> new GeneralGetRequest(gdpr: gdpr) }, + { Integer gdpr -> new GeneralGetRequest(gdprApplies: gdpr) }, + { Integer gdpr -> new GeneralGetRequest(gdpr: gdpr, gdprApplies: PBSUtils.randomBoolean) }, + ] + } + + def "PBS should apply usp from general get request when it's specified"() { + given: "Default General get request" + def usp = new CcpaConsent().getConsentString() + def generalGetRequest = GeneralGetRequest.default.tap { + it.usPrivacy = usp + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = privacyPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain usPrivacy from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.regs.usPrivacy == usp + } + + def "PBS should apply addtl_consent from general get request when it's specified"() { + given: "Default General get request" + def addtlConsent = PBSUtils.randomString + def generalGetRequest = GeneralGetRequest.default.tap { + it.additionalConsent = addtlConsent + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = privacyPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain addtlConsent from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.user.ext.consentedProvidersSettings.consentedProviders == addtlConsent + } + + def "PBS should apply gpp from general get request when it's specified"() { + given: "Default General get request" + def gppConsent = SIMPLE_GPC_DISALLOW_LOGIC.toString() + def generalGetRequest = GeneralGetRequest.default.tap { + it.gpp = gppConsent + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = privacyPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain regs.gpp from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.regs.gpp == gppConsent + } + + def "PBS should apply gpp section ids from general get request when it's specified"() { + given: "Default General get request" + def gppSids = [PBSUtils.getRandomEnum(GppSectionId.class, [TCF_EU_V2, HEADER_V1])].intValue + def generalGetRequest = GeneralGetRequest.default.tap { + it.gppSid = gppSids + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = privacyPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain gpp_sid from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.regs.gppSid == gppSids + } + + def "PBS should apply coppa from general get request when it's specified"() { + given: "Default General get request" + def coppa = PBSUtils.randomBinary + def generalGetRequest = GeneralGetRequest.default.tap { + it.coppa = coppa + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = privacyPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain coppa from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.regs.coppa == coppa + } + + def "PBS should apply gpc from general get request when it's specified"() { + given: "Default General get request" + def gpc = PBSUtils.randomBinary + def generalGetRequest = GeneralGetRequest.default.tap { + it.globalPrivacyControl = gpc + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest() + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = privacyPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain gpc from param" + def bidderRequest = bidder.getBidderRequest(request.id) + assert bidderRequest.regs.ext.gpc as Integer == gpc + } + + def "PBS should use original values from stored request when it's not specified in get request"() { + given: "Default General get request" + def storedRequestId = PBSUtils.randomString + def generalGetRequest = new GeneralGetRequest(storedRequestId: storedRequestId) + + and: "Default stored request" + def userForRequest = User.defaultUser.tap { + it.consent = new TcfConsent.Builder().build().toString() + it.ext = new UserExt(consentedProvidersSettings: new ConsentedProvidersSettings(consentedProviders: PBSUtils.randomString)) + } + def regsForRequest = new Regs().tap { + it.gdpr = 0 // for preventing bidder block + it.gpp = SIMPLE_GPC_DISALLOW_LOGIC + it.usPrivacy = new CcpaConsent().getConsentString() + it.gppSid = [PBSUtils.randomNumber] + it.ext = new RegsExt(gpc: PBSUtils.randomNumber) + it.coppa = 0 + } + def request = BidRequest.getDefaultBidRequest().tap { + user = userForRequest + regs = regsForRequest + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + when: "PBS processes general get request" + def response = privacyPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Bidder request should contain privacy data from original request" + verifyAll (bidder.getBidderRequest(request.id)) { + it.user.consent == userForRequest.consent + it.user.ext.consentedProvidersSettings == userForRequest.ext.consentedProvidersSettings + it.regs == regsForRequest + } + } + + def "PBS should properly process privacy functionality when it's required"() { + given: "Default General get request" + def consentValue = new TcfConsent.Builder().build().toString() + def accountId = PBSUtils.randomNumber.toString() + def generalGetRequest = (consentGeneralRequest(consentValue) as GeneralGetRequest).tap { + it.storedRequestId = PBSUtils.randomNumber + it.accountId = accountId + it.debug = DebugCondition.ENABLED + it.consentType = TCF_2 + it.gdpr = 1 + } + + and: "Default stored request" + def request = BidRequest.getDefaultBidRequest().tap { + setAccountId(accountId) + } + + and: "Save storedRequest into DB" + def storedRequest = StoredRequest.getStoredRequest(generalGetRequest.resolveStoredRequestId(), request) + storedRequestDao.save(storedRequest) + + and: "Save account config with requireConsent into DB" + def purposes = [(P2): new PurposeConfig(enforcePurpose: BASIC, enforceVendors: true)] + def accountGdprConfig = new AccountGdprConfig(purposes: purposes) + def account = getAccountWithGdpr(request.accountId, accountGdprConfig) + accountDao.save(account) + + when: "PBS processes general get request" + def response = privacyPbsService.sendGeneralGetRequest(generalGetRequest) + + then: "Response should not contain errors and warnings" + assert !response.ext?.errors + assert !response.ext?.warnings + + and: "Generic bidderRequest should contain tcfv2 info" + def bidderRequest = response.ext.debug.resolvedRequest + verifyAll (bidderRequest) { + it.user.consent == consentValue + it.regs.gdpr == 1 + } + + and: "Shouldn't contain other privacy info" + verifyAll (bidderRequest.regs) { + !it.coppa + !it.gpc + !it.usPrivacy + !it.gpp + } + + and: "Shouldn't contain ext privacy info" + assert bidderRequest.regs.ext == new RegsExt() + + and: "PBS should cancel request" + assert !bidder.getBidderRequests(request.id) + + and: "General Get Request with TCF_2 type correctly updates privacy enforcement metrics" + def metrics = privacyPbsService.sendCollectedMetricsRequest() + assert metrics[TEMPLATE_ADAPTER_DISALLOWED_COUNT.getValue(request, FETCH_BIDS)] == 1 + assert metrics[TEMPLATE_REQUEST_DISALLOWED_COUNT.getValue(request, FETCH_BIDS)] == 1 + + where: + consentGeneralRequest << + [ + { String gppConsent -> new GeneralGetRequest(tcfConsent: gppConsent) }, + { String gppConsent -> new GeneralGetRequest(generalConsent: gppConsent) }, + { String gppConsent -> new GeneralGetRequest(generalConsentString: gppConsent) }, + { String gppConsent -> new GeneralGetRequest(tcfConsent: gppConsent, generalConsent: PBSUtils.randomString) }, + { String gppConsent -> new GeneralGetRequest(tcfConsent: gppConsent, generalConsentString: PBSUtils.randomString) }, + { String gppConsent -> new GeneralGetRequest(generalConsentString: gppConsent, generalConsent: PBSUtils.randomString) } + ] + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppSyncUserActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppSyncUserActivitiesSpec.groovy index 2303bfc9e5f..e902a69b838 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppSyncUserActivitiesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppSyncUserActivitiesSpec.groovy @@ -2017,7 +2017,7 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { when: "PBS processes cookie sync request with header" def response = prebidServerService - .sendCookieSyncRequest(cookieSyncRequest, ["X-Forwarded-For": "209.232.44.21"]) + .sendCookieSyncRequest(cookieSyncRequest, ["X-Forwarded-For": USA_IP.v4]) then: "Response should contain bidders userSync.urls" assert response.getBidderUserSync(GENERIC).userSync.url @@ -2076,7 +2076,7 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { when: "PBS processes set uid request with header" def response = prebidServerService - .sendSetUidRequest(setuidRequest, uidsCookie, ["X-Forwarded-For": "209.232.44.21"]) + .sendSetUidRequest(setuidRequest, uidsCookie, ["X-Forwarded-For": USA_IP.v4]) then: "Response should contain uids cookie" assert response.uidsCookie @@ -2133,7 +2133,7 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { when: "PBS processes cookie sync request with header" def response = prebidServerService - .sendCookieSyncRequest(cookieSyncRequest, ["X-Forwarded-For": "209.232.44.21"]) + .sendCookieSyncRequest(cookieSyncRequest, ["X-Forwarded-For": USA_IP.v4]) then: "Response should not contain any URLs for bidders" assert !response.bidderStatus.userSync.url @@ -2190,7 +2190,7 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { accountDao.save(account) when: "PBS processes set uid request" - prebidServerService.sendSetUidRequest(setuidRequest, uidsCookie, ["X-Forwarded-For": "209.232.44.21"]) + prebidServerService.sendSetUidRequest(setuidRequest, uidsCookie, ["X-Forwarded-For": USA_IP.v4]) then: "Request should fail with error" def exception = thrown(PrebidServerException) diff --git a/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy b/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy index 748071dd10f..85abf5a07a9 100644 --- a/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy +++ b/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy @@ -23,6 +23,10 @@ class PBSUtils implements ObjectMapperWrapper { new Random().nextInt(upperBound - min) + min } + static int getRandomBinary() { + return new Random().nextInt(2) + } + static int getRandomNumberWithExclusion(int excludedValue, int min = 0, int max = MAX_VALUE) { def value = getRandomNumber(min, max) value == excludedValue ? getRandomNumberWithExclusion(excludedValue, min, max) : value @@ -175,4 +179,8 @@ class PBSUtils implements ObjectMapperWrapper { return false } } + + static Boolean isApproximatelyEqual(Integer actual, Integer expected, Integer tolerance = 100) { + Math.abs(actual - expected) <= tolerance + } }