diff --git a/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy b/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy index 352080844ab..54e0a451fc6 100644 --- a/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy @@ -18,6 +18,7 @@ enum BidderName { APPNEXUS("appnexus"), RUBICON_ALIAS("rubiconAlias"), OPENX("openx"), + OPENX_ALIAS("openxalias"), ACEEX("aceex"), ACUITYADS("acuityads"), AAX("aax"), diff --git a/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy index 4e423c03312..8dc9831e3fa 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming import groovy.transform.ToString import org.prebid.server.functional.model.bidder.BidderName import org.prebid.server.functional.model.request.auction.BidAdjustment +import org.prebid.server.functional.model.request.auction.PaaFormat import org.prebid.server.functional.model.request.auction.Targeting import org.prebid.server.functional.model.response.auction.MediaType @@ -23,6 +24,7 @@ class AccountAuctionConfig { AccountEventsConfig events AccountPriceFloorsConfig priceFloors Targeting targeting + PaaFormat paaformat @JsonProperty("preferredmediatype") Map preferredMediaType @JsonProperty("privacysandbox") diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/AuctionEnvironment.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/AuctionEnvironment.groovy new file mode 100644 index 00000000000..f880b7bc634 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/AuctionEnvironment.groovy @@ -0,0 +1,19 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.annotation.JsonValue +import org.prebid.server.functional.util.PBSUtils + +enum AuctionEnvironment { + + NOT_SUPPORTED(0), + DEVICE_ORCHESTRATED(1), + SERVER_ORCHESTRATED(3), + UNKNOWN(Integer.MAX_VALUE), + + @JsonValue + private int value + + AuctionEnvironment(Integer value) { + this.value = value + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Bidder.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Bidder.groovy index 605f286c803..05a43bbb528 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/Bidder.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Bidder.groovy @@ -22,6 +22,8 @@ class Bidder { AppNexus appNexus Openx openx Ix ix + @JsonProperty("openxalias") + Openx openxAlias static Bidder getDefaultBidder() { new Bidder().tap { diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExt.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExt.groovy index e817a4540a0..55c626d7bd6 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExt.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExt.groovy @@ -23,10 +23,13 @@ class ImpExt { String tid String gpid String sid - Integer ae + @JsonProperty("ae") + AuctionEnvironment auctionEnvironment String all String skadn String general + @JsonProperty("igs") + List interestGroupAuctionSupports AnyUnsupportedBidder anyUnsupportedBidder static ImpExt getDefaultImpExt() { diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/InterestGroupAuctionSupport.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/InterestGroupAuctionSupport.groovy new file mode 100644 index 00000000000..31d3628587d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/InterestGroupAuctionSupport.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.annotation.JsonProperty +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class InterestGroupAuctionSupport { + + @JsonProperty("ae") + AuctionEnvironment auctionEnvironment +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/PaaFormat.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/PaaFormat.groovy new file mode 100644 index 00000000000..79f41e953fe --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/PaaFormat.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.annotation.JsonValue + +enum PaaFormat { + + ORIGINAL, IAB, INVALID + + @JsonValue + String getValue() { + name().toLowerCase() + } +} 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 ba89f5680fa..0e021bb4598 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 @@ -40,6 +40,7 @@ class Prebid { PrebidModulesConfig modules PrebidAnalytics analytics StoredAuctionResponse storedAuctionResponse + PaaFormat paaFormat static class Channel { diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponseExt.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponseExt.groovy index 3ccb3384e89..d1d282d663a 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponseExt.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponseExt.groovy @@ -1,5 +1,6 @@ package org.prebid.server.functional.model.response.auction +import com.fasterxml.jackson.annotation.JsonProperty import groovy.transform.ToString import org.prebid.server.functional.model.response.BidderError import org.prebid.server.functional.model.response.Debug @@ -15,4 +16,6 @@ class BidResponseExt { Map usersync BidResponsePrebid prebid Map> warnings + @JsonProperty("igi") + List interestGroupAuctionIntent } diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionBuyer.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionBuyer.groovy new file mode 100644 index 00000000000..b8d1bf80d38 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionBuyer.groovy @@ -0,0 +1,17 @@ +package org.prebid.server.functional.model.response.auction + +import com.fasterxml.jackson.databind.PropertyNamingStrategies +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString +import org.prebid.server.functional.model.Currency + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategies.LowerCaseStrategy) +class InterestGroupAuctionBuyer { + + String origin + BigDecimal maxBid + Currency cur + Map pbs + InterestGroupAuctionBuyerExt ext +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionBuyerExt.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionBuyerExt.groovy new file mode 100644 index 00000000000..4404491246c --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionBuyerExt.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.response.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class InterestGroupAuctionBuyerExt { + + String bidder + String adapter +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionIntent.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionIntent.groovy new file mode 100644 index 00000000000..29ff15d4eeb --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionIntent.groovy @@ -0,0 +1,18 @@ +package org.prebid.server.functional.model.response.auction + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.PropertyNamingStrategies +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategies.LowerCaseStrategy) +class InterestGroupAuctionIntent { + + String impId + @JsonProperty("igb") + List interestGroupAuctionBuyer + @JsonProperty("igs") + List interestGroupAuctionSeller + InterestGroupAuctionIntentExt ext +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionIntentExt.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionIntentExt.groovy new file mode 100644 index 00000000000..29aeaf980da --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionIntentExt.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.response.auction + +import groovy.transform.ToString +import org.prebid.server.functional.model.bidder.BidderName + +@ToString(includeNames = true, ignoreNulls = true) +class InterestGroupAuctionIntentExt { + + BidderName bidder + BidderName adapter +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionSeller.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionSeller.groovy new file mode 100644 index 00000000000..192f5c2f611 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionSeller.groovy @@ -0,0 +1,14 @@ +package org.prebid.server.functional.model.response.auction + +import com.fasterxml.jackson.databind.PropertyNamingStrategies +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategies.LowerCaseStrategy) +class InterestGroupAuctionSeller { + + String impId + Map config + InterestGroupAuctionSellerExt ext +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionSellerExt.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionSellerExt.groovy new file mode 100644 index 00000000000..fcecf1b5480 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionSellerExt.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.response.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class InterestGroupAuctionSellerExt { + + String bidder + String adapter +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/OpenxBidResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/OpenxBidResponse.groovy index 2ba60b226c1..b5d5289d5a6 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/OpenxBidResponse.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/OpenxBidResponse.groovy @@ -1,6 +1,5 @@ package org.prebid.server.functional.model.response.auction - import groovy.transform.ToString import org.prebid.server.functional.model.request.auction.BidRequest 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 cab50bd816b..61774594b1f 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy @@ -6,6 +6,7 @@ import org.prebid.server.functional.model.db.Account import org.prebid.server.functional.model.db.StoredImp import org.prebid.server.functional.model.db.StoredRequest import org.prebid.server.functional.model.request.amp.AmpRequest +import org.prebid.server.functional.model.request.auction.AuctionEnvironment import org.prebid.server.functional.model.request.auction.Banner import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.request.auction.Device @@ -15,6 +16,7 @@ import org.prebid.server.functional.model.request.auction.Imp import org.prebid.server.functional.model.request.auction.ImpExt import org.prebid.server.functional.model.request.auction.ImpExtContext import org.prebid.server.functional.model.request.auction.ImpExtContextData +import org.prebid.server.functional.model.request.auction.InterestGroupAuctionSupport import org.prebid.server.functional.model.request.auction.Native import org.prebid.server.functional.model.request.auction.PrebidStoredRequest import org.prebid.server.functional.model.request.auction.Site @@ -34,6 +36,10 @@ import static org.prebid.server.functional.model.bidder.BidderName.APPNEXUS import static org.prebid.server.functional.model.bidder.CompressionType.GZIP import static org.prebid.server.functional.model.bidder.CompressionType.NONE import static org.prebid.server.functional.model.request.auction.Asset.titleAsset +import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.DEVICE_ORCHESTRATED +import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.NOT_SUPPORTED +import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.SERVER_ORCHESTRATED +import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.UNKNOWN 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 @@ -781,7 +787,7 @@ class BidderParamsSpec extends BaseSpec { def impExt = ImpExt.getDefaultImpExt().tap { prebid.bidder.generic = null generic = new Generic() - ae = PBSUtils.randomNumber + auctionEnvironment = PBSUtils.getRandomEnum(AuctionEnvironment) all = PBSUtils.randomNumber context = new ImpExtContext(data: new ImpExtContextData()) data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) @@ -801,7 +807,7 @@ class BidderParamsSpec extends BaseSpec { def bidderRequest = bidder.getBidderRequest(bidRequest.id) verifyAll(bidderRequest.imp[0].ext) { bidder == impExt.generic - ae == impExt.ae + auctionEnvironment == impExt.auctionEnvironment all == impExt.all context == impExt.context data == impExt.data @@ -847,7 +853,7 @@ class BidderParamsSpec extends BaseSpec { def "PBS should send request to bidder when adapters.bidder.aliases.bidder.meta-info.currency-accepted not specified"() { given: "PBS with adapter configuration" def pbsService = pbsServiceFactory.getService( - "adapters.generic.aliases.alias.enabled" : "true", + "adapters.generic.aliases.alias.enabled": "true", "adapters.generic.aliases.alias.endpoint": "$networkServiceContainer.rootUri/auction".toString(), "adapters.generic.aliases.alias.meta-info.currency-accepted": "") @@ -955,7 +961,7 @@ class BidderParamsSpec extends BaseSpec { def "PBS should send request to bidder when adapters.bidder.aliases.bidder.meta-info.currency-accepted intersect with requested currency"() { given: "PBS with adapter configuration" def pbsService = pbsServiceFactory.getService( - "adapters.generic.aliases.alias.enabled" : "true", + "adapters.generic.aliases.alias.enabled": "true", "adapters.generic.aliases.alias.endpoint": "$networkServiceContainer.rootUri/auction".toString(), "adapters.generic.aliases.alias.meta-info.currency-accepted": "${USD},${EUR}".toString()) @@ -996,7 +1002,7 @@ class BidderParamsSpec extends BaseSpec { def "PBS shouldn't send request to bidder and emit warning when adapters.bidder.aliases.bidder.meta-info.currency-accepted not intersect with requested currency"() { given: "PBS with adapter configuration" def pbsService = pbsServiceFactory.getService( - "adapters.generic.aliases.alias.enabled" : "true", + "adapters.generic.aliases.alias.enabled": "true", "adapters.generic.aliases.alias.endpoint": "$networkServiceContainer.rootUri/auction".toString(), "adapters.generic.aliases.alias.meta-info.currency-accepted": "${JPY},${CHF}".toString()) @@ -1040,4 +1046,66 @@ class BidderParamsSpec extends BaseSpec { assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY } + + def "PBS should add auction environment to imp.ext.igs when it is present in imp.ext and imp.ext.igs is empty"() { + given: "Default bid request with populated imp.ext" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.tap { + auctionEnvironment = requestedAuctionEnvironment + interestGroupAuctionSupports = [new InterestGroupAuctionSupport(auctionEnvironment: null)] + } + } + + when: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should imp[].{ae/ext.igs.ae} same value as requested" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.imp[0].ext.auctionEnvironment == requestedAuctionEnvironment + assert bidderRequest.imp[0].ext.interestGroupAuctionSupports[0].auctionEnvironment == requestedAuctionEnvironment + + where: + requestedAuctionEnvironment << [NOT_SUPPORTED, DEVICE_ORCHESTRATED] + } + + def "PBS shouldn't add unsupported auction environment to imp.ext.igs when it is present in imp.ext and imp.ext.igs is empty"() { + given: "Default bid request with populated imp.ext" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.tap { + auctionEnvironment = requestedAuctionEnvironment + interestGroupAuctionSupports = [new InterestGroupAuctionSupport(auctionEnvironment: null)] + } + } + + when: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should imp[].ae same value as requested" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.imp[0].ext.auctionEnvironment == requestedAuctionEnvironment + assert !bidderRequest.imp[0].ext.interestGroupAuctionSupports[0].auctionEnvironment + + where: + requestedAuctionEnvironment << [SERVER_ORCHESTRATED, UNKNOWN] + } + + def "PBS shouldn't change auction environment in imp.ext.igs when it is present in both imp.ext and imp.ext.igs"() { + given: "Default bid request with populated imp.ext" + def extAuctionEnv = PBSUtils.getRandomEnum(AuctionEnvironment) + def extIgsAuctionEnv = PBSUtils.getRandomEnum(AuctionEnvironment) + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.tap { + auctionEnvironment = extAuctionEnv + interestGroupAuctionSupports = [new InterestGroupAuctionSupport(auctionEnvironment: extIgsAuctionEnv)] + } + } + + when: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should imp[].{ae/ext.igs.ae} same value as requested" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.imp[0].ext.auctionEnvironment == extAuctionEnv + assert bidderRequest.imp[0].ext.interestGroupAuctionSupports[0].auctionEnvironment == extIgsAuctionEnv + } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/bidder/openx/OpenxSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/bidder/openx/OpenxSpec.groovy index 45540134252..1adf84c32fd 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/bidder/openx/OpenxSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/bidder/openx/OpenxSpec.groovy @@ -1,35 +1,95 @@ package org.prebid.server.functional.tests.bidder.openx +import org.prebid.server.functional.model.Currency import org.prebid.server.functional.model.bidder.Openx +import org.prebid.server.functional.model.config.AccountAuctionConfig +import org.prebid.server.functional.model.config.AccountConfig +import org.prebid.server.functional.model.db.Account import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.PaaFormat +import org.prebid.server.functional.model.response.auction.InterestGroupAuctionBuyer +import org.prebid.server.functional.model.response.auction.InterestGroupAuctionBuyerExt +import org.prebid.server.functional.model.response.auction.InterestGroupAuctionIntent +import org.prebid.server.functional.model.response.auction.InterestGroupAuctionSeller import org.prebid.server.functional.model.response.auction.OpenxBidResponse import org.prebid.server.functional.model.response.auction.OpenxBidResponseExt +import org.prebid.server.functional.service.PrebidServerException import org.prebid.server.functional.service.PrebidServerService import org.prebid.server.functional.tests.BaseSpec import org.prebid.server.functional.util.PBSUtils import spock.lang.Shared +import java.time.Instant + +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.bidder.BidderName.OPENX_ALIAS +import static org.prebid.server.functional.model.bidder.BidderName.WILDCARD +import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.DEVICE_ORCHESTRATED +import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.NOT_SUPPORTED +import static org.prebid.server.functional.model.request.auction.PaaFormat.IAB +import static org.prebid.server.functional.model.request.auction.PaaFormat.ORIGINAL +import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID import static org.prebid.server.functional.testcontainers.Dependencies.networkServiceContainer class OpenxSpec extends BaseSpec { private static final Map OPENX_CONFIG = ["adapters.openx.enabled" : "true", "adapters.openx.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + private static final Map OPENX_ALIAS_CONFIG = ["adapters.openx.aliases.openxalias.enabled" : "true", + "adapters.openx.aliases.openxalias.endpoint": "$networkServiceContainer.rootUri/auction".toString()] @Shared PrebidServerService pbsService = pbsServiceFactory.getService(OPENX_CONFIG) - def "PBS should populate fledge config when bid response with fledge and imp[0].ext.ae = 1"() { + def "PBS should populate fledge config by default when bid response with fledge"() { given: "Default basic BidRequest with ae and openx bidder" def bidRequest = BidRequest.defaultBidRequest.tap { - imp[0].ext.ae = 1 + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx } and: "Default bid response with fledge config" def impId = bidRequest.imp[0].id def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] - def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest).tap { + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX).tap { + ext = new OpenxBidResponseExt().tap { + fledgeAuctionConfigs = [(impId): fledgeConfig] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response should contain fledge config" + def auctionConfigs = response.ext?.prebid?.fledge?.auctionConfigs + assert auctionConfigs?.size() == 1 + assert auctionConfigs[0].impId == impId + assert auctionConfigs[0].bidder == bidResponse.seatbid[0].seat.value + assert auctionConfigs[0].adapter == bidResponse.seatbid[0].seat.value + assert auctionConfigs[0].config == fledgeConfig + + and: "PBS response shouldn't contain igb config" + assert !response.ext?.interestGroupAuctionIntent?.interestGroupAuctionBuyer + + and: "PBS response shouldn't contain igs config" + assert !response.ext?.interestGroupAuctionIntent?.interestGroupAuctionSeller + } + + def "PBS should populate fledge config when bid response with fledge and ext.prebid.paaFormat = ORIGINAL"() { + given: "Default basic BidRequest with ae and openx bidder" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED + imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + ext.prebid.paaFormat = ORIGINAL + } + + and: "Default bid response with fledge config" + def impId = bidRequest.imp[0].id + def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX).tap { ext = new OpenxBidResponseExt().tap { fledgeAuctionConfigs = [(impId): fledgeConfig] } @@ -48,13 +108,61 @@ class OpenxSpec extends BaseSpec { assert auctionConfigs[0].bidder == bidResponse.seatbid[0].seat.value assert auctionConfigs[0].adapter == bidResponse.seatbid[0].seat.value assert auctionConfigs[0].config == fledgeConfig + + and: "PBS response shouldn't contain igb config" + assert !response.ext?.interestGroupAuctionIntent?.interestGroupAuctionBuyer + + and: "PBS response shouldn't contain igs config" + assert !response.ext?.interestGroupAuctionIntent?.interestGroupAuctionSeller } - def "PBS shouldn't populate fledge config when imp[0].ext.ae = 0"() { + def "PBS should take precedence request paa format over account value when both specified"() { + given: "Default bid request with openx" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + ext.prebid.paaFormat = IAB + } + + and: "Default bid response with fledge config" + def impId = bidRequest.imp[0].id + def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest).tap { + ext = new OpenxBidResponseExt().tap { + fledgeAuctionConfigs = [(impId): fledgeConfig] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + and: "Save account in the DB" + def accountConfig = new AccountConfig(auction: new AccountAuctionConfig(paaformat: ORIGINAL)) + def account = new Account(uuid: bidRequest.site.publisher.id, config: accountConfig) + accountDao.save(account) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response shouldn't contain fledge config" + assert !response.ext?.prebid?.fledge?.auctionConfigs + + and: "PBS response should contain igs config" + def interestGroupAuctionSeller = response.ext.interestGroupAuctionIntent[0].interestGroupAuctionSeller[0] + assert interestGroupAuctionSeller.impId == impId + assert interestGroupAuctionSeller.config == fledgeConfig + assert interestGroupAuctionSeller.ext.bidder == bidResponse.seatbid[0].seat.value + assert interestGroupAuctionSeller.ext.adapter == bidResponse.seatbid[0].seat.value + + and: "PBS response shouldn't contain igb config" + assert !response.ext?.interestGroupAuctionIntent?[0]?.interestGroupAuctionBuyer + } + + def "PBS shouldn't populate fledge config when bid response with fledge and ext.prebid.paaFormat = IAB"() { given: "Default basic BidRequest without ae" def bidRequest = BidRequest.defaultBidRequest.tap { - imp[0].ext.ae = 0 + imp[0].ext.auctionEnvironment = NOT_SUPPORTED imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + ext.prebid.paaFormat = IAB } and: "Default bid response" @@ -73,12 +181,21 @@ class OpenxSpec extends BaseSpec { then: "PBS response shouldn't contain fledge config" assert !response.ext.prebid.fledge + + and: "PBS response shouldn't contain igb config" + assert !response.ext?.interestGroupAuctionIntent?[0]?.interestGroupAuctionBuyer + + and: "PBS response should contain igs config" + def interestGroupAuctionSeller = response.ext.interestGroupAuctionIntent[0].interestGroupAuctionSeller[0] + assert interestGroupAuctionSeller.impId == impId + assert interestGroupAuctionSeller.config + assert interestGroupAuctionSeller.ext.bidder == bidResponse.seatbid[0].seat.value + assert interestGroupAuctionSeller.ext.adapter == OPENX.value } - def "PBS shouldn't populate fledge config when imp[0].ext.ae = 1 and bid response didn't return fledge config"() { + def "PBS shouldn't populate fledge config when bid response didn't return fledge config"() { given: "Default basic BidRequest without ae" def bidRequest = BidRequest.defaultBidRequest.tap { - imp[0].ext.ae = 1 imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx } @@ -97,5 +214,324 @@ class OpenxSpec extends BaseSpec { then: "PBS response shouldn't contain fledge config" assert !response.ext.prebid.fledge + + and: "PBS response shouldn't contain igi config" + assert !response?.ext?.interestGroupAuctionIntent + } + + def "PBS should populate fledge and iab output config when bid response with fledge and paa formant IAB"() { + given: "Default bid request with openx" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + ext.prebid.paaFormat = requestPaaFormant + } + + and: "Default bid response with fledge config" + def impId = bidRequest.imp[0].id + def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest).tap { + ext = new OpenxBidResponseExt().tap { + fledgeAuctionConfigs = [(impId): fledgeConfig] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + and: "Save account in the DB" + def accountConfig = new AccountConfig(auction: new AccountAuctionConfig(paaformat: accountPaaFormat)) + def account = new Account(uuid: bidRequest.site.publisher.id, config: accountConfig) + accountDao.save(account) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response shouldn't contain fledge config" + assert !response.ext?.prebid?.fledge?.auctionConfigs + + and: "PBS response should contain igs config" + def interestGroupAuctionSeller = response.ext.interestGroupAuctionIntent[0].interestGroupAuctionSeller[0] + assert interestGroupAuctionSeller.impId == impId + assert interestGroupAuctionSeller.config == fledgeConfig + assert interestGroupAuctionSeller.ext.bidder == bidResponse.seatbid[0].seat.value + assert interestGroupAuctionSeller.ext.adapter == bidResponse.seatbid[0].seat.value + + and: "PBS response shouldn't contain igb config" + assert !response.ext?.interestGroupAuctionIntent?[0]?.interestGroupAuctionBuyer + + where: + accountPaaFormat | requestPaaFormant + IAB | IAB + null | IAB + IAB | null + } + + def "PBS should populate fledge config by default when bid response with fledge and requested aliases"() { + given: "PBS config with alias config" + def pbsService = pbsServiceFactory.getService(OPENX_CONFIG + OPENX_ALIAS_CONFIG) + + and: "Default basic BidRequest with ae and bidder" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED + imp[0].ext.prebid.bidder.generic = null + imp[0].ext.prebid.bidder.openxAlias = Openx.defaultOpenx + ext.prebid.aliases = [(OPENX_ALIAS.value): OPENX] + } + + and: "Default bid response with fledge config" + def impId = bidRequest.imp[0].id + def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX_ALIAS).tap { + ext = new OpenxBidResponseExt().tap { + fledgeAuctionConfigs = [(impId): fledgeConfig] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response should contain fledge config" + def auctionConfigs = response.ext?.prebid?.fledge?.auctionConfigs + assert auctionConfigs?.size() == 1 + assert auctionConfigs[0].impId == impId + assert auctionConfigs[0].bidder == OPENX_ALIAS.value + assert auctionConfigs[0].adapter == OPENX.value + assert auctionConfigs[0].config == fledgeConfig + + and: "PBS response shouldn't contain igi config" + assert !response.ext?.interestGroupAuctionIntent + } + + def "PBS should populate iab config when bid response with fledge and requested aliases"() { + given: "PBS config" + def pbsService = pbsServiceFactory.getService(OPENX_CONFIG + OPENX_ALIAS_CONFIG) + + and: "Default basic BidRequest with ae and openx bidder" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED + imp[0].ext.prebid.bidder.generic = null + imp[0].ext.prebid.bidder.openxAlias = Openx.defaultOpenx + ext.prebid.aliases = [(OPENX_ALIAS.value): OPENX] + ext.prebid.paaFormat = IAB + } + + and: "Default bid response with fledge config" + def impId = bidRequest.imp[0].id + def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX_ALIAS).tap { + ext = new OpenxBidResponseExt().tap { + fledgeAuctionConfigs = [(impId): fledgeConfig] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response shouldn't contain fledge config" + assert !response.ext?.prebid?.fledge?.auctionConfigs + + and: "PBS response should contain igs config" + def interestGroupAuctionSeller = response.ext.interestGroupAuctionIntent[0].interestGroupAuctionSeller[0] + assert interestGroupAuctionSeller.impId == impId + assert interestGroupAuctionSeller.config == fledgeConfig + assert interestGroupAuctionSeller.ext.bidder == OPENX_ALIAS.value + assert interestGroupAuctionSeller.ext.adapter == OPENX.value + + and: "PBS response shouldn't contain igi config" + assert !response.ext?.interestGroupAuctionIntent?[0].interestGroupAuctionBuyer + } + + def "PBS should populate fledge config by default when bid response with fledge and imp mismatched"() { + given: "Default basic BidRequest with ae and openx bidder" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED + imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + } + + and: "Default bid response with fledge config" + def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX).tap { + ext = new OpenxBidResponseExt().tap { + fledgeAuctionConfigs = [(fledgeImpId): fledgeConfig] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response should contain fledge config" + def auctionConfigs = response.ext?.prebid?.fledge?.auctionConfigs + assert auctionConfigs?.size() == 1 + assert auctionConfigs[0].impId == fledgeImpId + assert auctionConfigs[0].bidder == bidResponse.seatbid[0].seat.value + assert auctionConfigs[0].adapter == bidResponse.seatbid[0].seat.value + assert auctionConfigs[0].config == fledgeConfig + + and: "PBS response shouldn't contain igi config" + assert !response.ext?.interestGroupAuctionIntent + + where: + fledgeImpId << [PBSUtils.randomString, PBSUtils.randomNumber as String, WILDCARD.value] + } + + def "PBS should log error and not populated fledge impId when bidder respond with not empty config, but an empty impid"() { + given: "Start time" + def startTime = Instant.now() + + and: "Default basic BidRequest with ae and openx bidder" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED + imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + ext.prebid.paaFormat = IAB + } + + and: "Default bid response with fledge config without imp" + def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX).tap { + ext = new OpenxBidResponseExt().tap { + fledgeAuctionConfigs = [(""): fledgeConfig] as Map + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + and: "Flush metrics" + flushMetrics(pbsService) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response shouldn't contain fledge config" + assert !response.ext?.prebid?.fledge?.auctionConfigs + + and: "PBS response shouldn't contain igi config" + assert !response.ext?.interestGroupAuctionIntent + + and: "PBS log should contain error" + def logs = pbsService.getLogsByTime(startTime) + assert getLogsByText(logs, "ExtIgiIgs with absent impId from bidder: ${OPENX.value}") + + and: "Bid response should contain warning" + assert response.ext.warnings[PREBID]?.code == [999] + assert response.ext.warnings[PREBID]?.message == + ["ExtIgiIgs with absent impId from bidder: ${OPENX.value}" as String] + + and: "Alert.general metric should be updated" + def metrics = pbsService.sendCollectedMetricsRequest() + assert metrics["alerts.general" as String] == 1 + } + + def "PBS shouldn't populate fledge or igi config when bidder respond with igb"() { + given: "Default basic BidRequest with ae and openx bidder" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED + imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + } + + and: "Default bid response with igb config" + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX).tap { + ext = new OpenxBidResponseExt().tap { + interestGroupAuctionIntent = [new InterestGroupAuctionIntent( + interestGroupAuctionBuyer: [new InterestGroupAuctionBuyer( + origin: PBSUtils.randomString, + maxBid: PBSUtils.randomDecimal, + cur: PBSUtils.getRandomEnum(Currency), + pbs: [(PBSUtils.randomString): PBSUtils.randomString], + ext: new InterestGroupAuctionBuyerExt( + bidder: PBSUtils.randomString, + adapter: PBSUtils.randomString + ) + )]) + ] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response should contain fledge config" + assert !response.ext?.prebid?.fledge?.auctionConfigs + + and: "PBS response shouldn't contain igi config" + assert !response.ext?.interestGroupAuctionIntent + } + + def "PBS should throw error when requested unknown paa format"() { + given: "Default basic BidRequest with ae and openx bidder" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED + imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + ext.prebid.paaFormat = PaaFormat.INVALID + } + + and: "Default bid response with fledge config" + def impId = bidRequest.imp[0].id + def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX).tap { + ext = new OpenxBidResponseExt().tap { + fledgeAuctionConfigs = [(impId): fledgeConfig] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + pbsService.sendAuctionRequest(bidRequest) + + then: "Request should fail with error" + def exception = thrown(PrebidServerException) + assert exception.responseBody.startsWith("Invalid request format: Error decoding bidRequest: " + + "Cannot deserialize value of type `org.prebid.server.auction.model.PaaFormat` " + + "from String \"invalid\": not one of the values accepted for Enum class: [original, iab]") + } + + def "PBS shouldn't cause error when igs and igb empty array"() { + given: "Default basic BidRequest with ae and openx bidder" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED + imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + ext.prebid.paaFormat = paaFormat + } + + and: "Default bid response with igs config" + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX).tap { + ext = new OpenxBidResponseExt().tap { + interestGroupAuctionIntent = [new InterestGroupAuctionIntent( + interestGroupAuctionSeller: interestGroupAuctionSeller, + interestGroupAuctionBuyer: interestGroupAuctionBuyer + )] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response should contain fledge config" + assert !response.ext?.prebid?.fledge?.auctionConfigs + + and: "PBS response shouldn't contain igi config" + assert !response.ext?.interestGroupAuctionIntent + + where: + paaFormat | interestGroupAuctionSeller | interestGroupAuctionBuyer + IAB | [new InterestGroupAuctionSeller()] | [new InterestGroupAuctionBuyer()] + ORIGINAL | [] | [] } }