From 65d0bea0c02d478d8e4ba3344ecbf38b38e93cb3 Mon Sep 17 00:00:00 2001 From: markiian Date: Tue, 25 Mar 2025 12:52:29 +0200 Subject: [PATCH 1/7] Update alternate bidder code --- .../tests/AlternateBidderCodeSpec.groovy | 121 +++++++++++++++++- .../functional/tests/BidAdjustmentSpec.groovy | 41 ++++++ .../tests/bidder/openx/OpenxSpec.groovy | 11 +- .../pricefloors/PriceFloorsRulesSpec.groovy | 66 +++++++++- 4 files changed, 228 insertions(+), 11 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy index a1ab76389aa..4531a3a7724 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy @@ -7,6 +7,7 @@ import org.prebid.server.functional.model.config.BidderConfig import org.prebid.server.functional.model.db.Account import org.prebid.server.functional.model.request.auction.Amx import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.Imp import org.prebid.server.functional.model.request.auction.Targeting import org.prebid.server.functional.model.response.auction.BidExt import org.prebid.server.functional.model.response.auction.BidResponse @@ -269,7 +270,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == AMX.value + assert seatNonBid.seat == bidderName.value assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -324,7 +325,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == ALIAS.value + assert seatNonBid.seat == bidderName.value assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -782,7 +783,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == AMX.value + assert seatNonBid.seat == UNKNOWN.value assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -838,7 +839,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == AMX.value + assert seatNonBid.seat == UNKNOWN.value assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -1182,7 +1183,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == AMX.value + assert seatNonBid.seat == requestedAllowedBidderCode.value assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -1237,7 +1238,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == AMX.value + assert seatNonBid.seat == requestedAllowedBidderCode.value assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -1298,7 +1299,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == AMX.value + assert seatNonBid.seat == allowedBidderCodes.value assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -1572,6 +1573,112 @@ class AlternateBidderCodeSpec extends BaseSpec { pbsServiceFactory.removeContainer(pbsConfig) } + def "PBS should do something when different bidder response with same seat"() { + given: "Default bid request with amx and generic bidder" + def bidRequest = getBidRequestWithAmxBidderAndAlternateBidderCode().tap { + imp[0].ext.prebid.bidder.generic = new Generic() + ext.prebid.alternateBidderCodes.bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC])] + } + + and: "Bid response with bidder code" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap { + it.seatbid[0].bid[0].ext = new BidExt(bidderCode: GENERIC) + } + bidder.setResponse(bidRequest.id, bidResponse) + + and: "Flash metrics" + flushMetrics(pbsServiceWithAmxBidder) + + when: "PBS processes auction request" + def response = pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seat" + assert response.seatbid.seat == [GENERIC] + + and: "Response should contain adapter code" + assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX] + + and: "Response should contain bidder targeting" + def targeting = response.seatbid[0].bid[0].ext.prebid.targeting + assert targeting["hb_pb_${GENERIC}"] + assert targeting["hb_size_${GENERIC}"] + assert targeting["hb_bidder"] == GENERIC.value + assert targeting["hb_bidder_${GENERIC}"] == GENERIC.value + + and: "Response should contain repose millis with corresponding bidder" + assert response.ext.responsetimemillis.containsKey(GENERIC.value) + + and: "Bidder request should be valid" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Response shouldn't contain warnings and error and seatNonBid" + assert !response.ext?.warnings + assert !response.ext?.errors + assert !response.ext?.seatnonbid + + and: "Response shouldn't contain demand source" + assert !response.seatbid.first.bid.first.ext.prebid.meta.demandSource + + and: "PBS shouldn't emit validation metrics" + def metrics = pbsServiceWithAmxBidder.sendCollectedMetricsRequest() + assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(AMX)] + } + + def "PBS should do something when same bidder response with different bidder code"() { + given: "Default bid request with amx and generic bidder" + def bidRequest = getBidRequestWithAmxBidderAndAlternateBidderCode().tap { + imp[0].ext.prebid.bidder.amx = new Amx() + imp.add(Imp.getDefaultImpression()) + imp[1].ext.prebid.bidder.amx = new Amx() + imp[1].ext.prebid.bidder.generic = null + ext.prebid.alternateBidderCodes.bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC, AMX])] + } + + and: "Bid response with bidder code" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap { + it.seatbid[0].bid[0].ext = new BidExt(bidderCode: GENERIC) + it.seatbid[0].bid[1].ext = new BidExt(bidderCode: AMX) + } + bidder.setResponse(bidRequest.id, bidResponse) + + and: "Flash metrics" + flushMetrics(pbsServiceWithAmxBidder) + + when: "PBS processes auction request" + def response = pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seat" + assert response.seatbid.seat == [GENERIC, AMX] + + and: "Response should contain adapter code" + assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX, GENERIC, GENERIC] + + and: "Response should contain bidder targeting" + def targeting = response.seatbid[0].bid[0].ext.prebid.targeting + assert targeting["hb_pb_${GENERIC}"] + assert targeting["hb_size_${GENERIC}"] + assert targeting["hb_bidder"] == GENERIC.value + assert targeting["hb_bidder_${GENERIC}"] == GENERIC.value + + and: "Response should contain repose millis with corresponding bidder" + assert response.ext.responsetimemillis.containsKey(GENERIC.value) + + and: "Bidder request should be valid" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Response shouldn't contain warnings and error and seatNonBid" + assert !response.ext?.warnings + assert !response.ext?.errors + assert !response.ext?.seatnonbid + + and: "Response shouldn't contain demand source" + assert !response.seatbid.first.bid.first.ext.prebid.meta.demandSource + + and: "PBS shouldn't emit validation metrics" + def metrics = pbsServiceWithAmxBidder.sendCollectedMetricsRequest() + assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(AMX)] + } + private static Account getAccountWithAlternateBidderCode(BidRequest bidRequest) { new Account().tap { it.uuid = bidRequest.accountId diff --git a/src/test/groovy/org/prebid/server/functional/tests/BidAdjustmentSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/BidAdjustmentSpec.groovy index 43b7a09777a..8effa51b231 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/BidAdjustmentSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/BidAdjustmentSpec.groovy @@ -1,6 +1,7 @@ package org.prebid.server.functional.tests import org.prebid.server.functional.model.Currency +import org.prebid.server.functional.model.bidder.Generic import org.prebid.server.functional.model.config.AccountAuctionConfig import org.prebid.server.functional.model.config.AccountConfig import org.prebid.server.functional.model.config.AlternateBidderCodes @@ -31,6 +32,7 @@ import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST import static org.prebid.server.functional.model.Currency.EUR import static org.prebid.server.functional.model.Currency.GBP import static org.prebid.server.functional.model.Currency.USD +import static org.prebid.server.functional.model.bidder.BidderName.ALIAS import static org.prebid.server.functional.model.bidder.BidderName.AMX import static org.prebid.server.functional.model.bidder.BidderName.APPNEXUS import static org.prebid.server.functional.model.bidder.BidderName.GENERIC @@ -1152,6 +1154,45 @@ class BidAdjustmentSpec extends BaseSpec { bidAdjustmentFactor << [0.9, 1.1] } + def "PBS should prefer bid price adjustment based on media type and alternate bidder code when request has per-media-type bid adjustment factors with soft alias"() { + given: "Default bid request with bid adjustment" + def bidRequest = BidRequest.getDefaultBidRequest(SITE).tap { + ext.prebid.aliases = [(ALIAS.value): AMX] + imp[0].ext.prebid.bidder.generic = null + imp[0].ext.prebid.bidder.amx = null + imp[0].ext.prebid.bidder.alias = new Generic() + ext.prebid.tap { + bidAdjustmentFactors = new BidAdjustmentFactors().tap { + adjustments = [(GENERIC): randomDecimal] + mediaTypes = [(BANNER): [(GENERIC): bidAdjustmentFactor]] + } + alternateBidderCodes = new AlternateBidderCodes().tap { + enabled = true + bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC])] + } + } + } + + and: "Bid response with bidder code" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap { + it.seatbid[0].bid[0].ext = new BidExt(bidderCode: GENERIC) + } + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "Final bid price should be adjusted" + assert response?.seatbid?.first?.bid?.first?.price == bidResponse.seatbid.first.bid.first.price * + bidAdjustmentFactor + + and: "Response should contain repose millis with corresponding bidder" + assert response.ext.responsetimemillis.containsKey(GENERIC.value) + + where: + bidAdjustmentFactor << [0.9, 1.1] + } + private static Map getExternalCurrencyConverterConfig() { ["auction.ad-server-currency" : DEFAULT_CURRENCY as String, "currency-converter.external-rates.enabled" : "true", 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 20be3434042..2c0141288e2 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 @@ -26,7 +26,6 @@ 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.* 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 @@ -307,7 +306,7 @@ class OpenxSpec extends BaseSpec { 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].adapter == OPENX_ALIAS.value assert auctionConfigs[0].config == fledgeConfig and: "PBS response shouldn't contain igi config" @@ -350,7 +349,13 @@ class OpenxSpec extends BaseSpec { assert interestGroupAuctionSeller.impId == impId assert interestGroupAuctionSeller.config == fledgeConfig assert interestGroupAuctionSeller.ext.bidder == OPENX_ALIAS.value - assert interestGroupAuctionSeller.ext.adapter == OPENX.value + assert interestGroupAuctionSeller.ext.adapter == OPENX_ALIAS.value + + and: "Response should contain seat" + assert response.seatbid[0].seat == OPENX_ALIAS + + and: "Response should contain seat" + assert response.seatbid[0].bid[0].ext.prebid.meta.adapterCode == OPENX_ALIAS and: "PBS response shouldn't contain igi config" assert !response.ext?.interestGroupAuctionIntent?[0].interestGroupAuctionBuyer diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsRulesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsRulesSpec.groovy index e493bbb7df9..ee861a24587 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsRulesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsRulesSpec.groovy @@ -4,6 +4,8 @@ import org.prebid.server.functional.model.ChannelType import org.prebid.server.functional.model.bidder.Generic import org.prebid.server.functional.model.bidder.Openx import org.prebid.server.functional.model.bidderspecific.BidderRequest +import org.prebid.server.functional.model.config.AlternateBidderCodes +import org.prebid.server.functional.model.config.BidderConfig import org.prebid.server.functional.model.db.StoredImp import org.prebid.server.functional.model.pricefloors.Country import org.prebid.server.functional.model.pricefloors.MediaType @@ -11,6 +13,7 @@ import org.prebid.server.functional.model.pricefloors.ModelGroup import org.prebid.server.functional.model.pricefloors.PriceFloorData import org.prebid.server.functional.model.pricefloors.PriceFloorSchema import org.prebid.server.functional.model.pricefloors.Rule +import org.prebid.server.functional.model.request.auction.Amx 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 @@ -22,11 +25,13 @@ import org.prebid.server.functional.model.request.auction.Imp import org.prebid.server.functional.model.request.auction.ImpExtContextData import org.prebid.server.functional.model.request.auction.ImpExtContextDataAdServer import org.prebid.server.functional.model.request.auction.PrebidStoredRequest +import org.prebid.server.functional.model.response.auction.BidExt import org.prebid.server.functional.model.response.auction.BidResponse import org.prebid.server.functional.util.PBSUtils import static org.prebid.server.functional.model.ChannelType.WEB import static org.prebid.server.functional.model.bidder.BidderName.ALIAS +import static org.prebid.server.functional.model.bidder.BidderName.AMX import static org.prebid.server.functional.model.bidder.BidderName.GENERIC import static org.prebid.server.functional.model.bidder.BidderName.OPENX import static org.prebid.server.functional.model.pricefloors.Country.USA @@ -1042,7 +1047,7 @@ class PriceFloorsRulesSpec extends PriceFloorsBaseSpec { and: "Bidder request should contain proper bid floor value" def bidderRequests = bidder.getBidderRequests(bidRequest.id) - def impIdToBidderCallImp = impIdToBidderCallImp(bidderRequests) + def impIdToBidderCallImp = impIdToBidderCallImp(bidderRequests) assert impIdToBidderCallImp[bidRequest.imp[0].id].bidFloor == genericBidFloorRuleValue assert impIdToBidderCallImp[bidRequest.imp[1].id].bidFloor == openxBidFloorRuleValue @@ -1145,6 +1150,65 @@ class PriceFloorsRulesSpec extends PriceFloorsBaseSpec { PBSUtils.randomFloorValue | PBSUtils.randomFloorValue } + def "PBS should populate seatNonBid when bid rejected due to floor and alternate bidder code specified"() { + given: "PBS config with floors config" + def config = FLOORS_CONFIG + + ["adapters.amx.enabled" : "true", + "adapters.amx.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + def pbsService = pbsServiceFactory.getService(config) + + and: "Default bid request" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.prebid.bidder.tap { + generic = null + amx = new Amx() + } + ext.prebid.tap { + floors = new ExtPrebidFloors(enforcement: new ExtPrebidPriceFloorEnforcement(enforcePbs: true)) + returnAllBidStatus = true + alternateBidderCodes = new AlternateBidderCodes( + enabled: true, + bidders: [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC])]) + } + } + + and: "Account with enabled fetch, fetch.url in the DB" + def account = getAccountWithEnabledFetch(bidRequest.site.publisher.id) + accountDao.save(account) + + and: "Set Floors Provider response" + def floorValue = PBSUtils.randomFloorValue + def floorsResponse = PriceFloorData.priceFloorData.tap { + modelGroups[0].values = [(rule): floorValue] + } + floorsProvider.setResponse(bidRequest.site.publisher.id, floorsResponse) + + and: "PBS cache rules" + cacheFloorsProviderRules(bidRequest, floorValue, pbsService, AMX) + + and: "Set bidder response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { + seatbid.first().bid.first().tap { + price = floorValue - 0.1 + ext = new BidExt(bidderCode: GENERIC) + } + } + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response should contain seatNonBid" + def seatNonBids = response.ext.seatnonbid + assert seatNonBids.size() == 1 + + def seatNonBid = seatNonBids[0] + assert seatNonBid.seat == GENERIC.value + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_DUE_TO_PRICE_FLOOR + assert seatNonBid.nonBid.size() == bidResponse.seatbid[0].bid.size() + } + private static Map impIdToBidderCallImp(List bidderRequests) { bidderRequests.imp.flatten().collectEntries { [it.id, it] } as Map } From 7cd5d532b4a520126de173c4d49491b66da52738 Mon Sep 17 00:00:00 2001 From: markiian Date: Tue, 25 Mar 2025 15:17:33 +0200 Subject: [PATCH 2/7] Update alternate bidder code --- .../tests/AlternateBidderCodeSpec.groovy | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy index 4531a3a7724..94bb1a51693 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy @@ -1651,17 +1651,25 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.seatbid.seat == [GENERIC, AMX] and: "Response should contain adapter code" - assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX, GENERIC, GENERIC] + assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX, AMX] and: "Response should contain bidder targeting" - def targeting = response.seatbid[0].bid[0].ext.prebid.targeting - assert targeting["hb_pb_${GENERIC}"] - assert targeting["hb_size_${GENERIC}"] - assert targeting["hb_bidder"] == GENERIC.value - assert targeting["hb_bidder_${GENERIC}"] == GENERIC.value + def genericTargeting = response.seatbid[0].bid[0].ext.prebid.targeting + assert genericTargeting["hb_pb_${GENERIC}"] + assert genericTargeting["hb_size_${GENERIC}"] + assert genericTargeting["hb_bidder"] == GENERIC.value + assert genericTargeting["hb_bidder_${GENERIC}"] == GENERIC.value + + and: "Response should contain bidder targeting" + def amxTargeting = response.seatbid[1].bid[0].ext.prebid.targeting + assert amxTargeting["hb_pb_${AMX}"] + assert amxTargeting["hb_size_${AMX}"] + assert amxTargeting["hb_bidder"] == AMX.value + assert amxTargeting["hb_bidder_${AMX}"] == AMX.value and: "Response should contain repose millis with corresponding bidder" assert response.ext.responsetimemillis.containsKey(GENERIC.value) + assert response.ext.responsetimemillis.containsKey(AMX.value) and: "Bidder request should be valid" assert bidder.getBidderRequests(bidRequest.id) @@ -1677,6 +1685,7 @@ class AlternateBidderCodeSpec extends BaseSpec { and: "PBS shouldn't emit validation metrics" def metrics = pbsServiceWithAmxBidder.sendCollectedMetricsRequest() assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(AMX)] + assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(GENERIC)] } private static Account getAccountWithAlternateBidderCode(BidRequest bidRequest) { From cbdc4512948bace2c7f0b31e8dc28ed02d88fb50 Mon Sep 17 00:00:00 2001 From: markiian Date: Wed, 26 Mar 2025 10:54:37 +0200 Subject: [PATCH 3/7] Update alternate bidder code --- .../tests/AlternateBidderCodeSpec.groovy | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy index 94bb1a51693..d86eaabbc12 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy @@ -1593,20 +1593,24 @@ class AlternateBidderCodeSpec extends BaseSpec { def response = pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest) then: "Bid response should contain seat" - assert response.seatbid.seat == [GENERIC] + assert response.seatbid.seat == [GENERIC, GENERIC] and: "Response should contain adapter code" - assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX] + assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten().sort() == [AMX, GENERIC].sort() and: "Response should contain bidder targeting" - def targeting = response.seatbid[0].bid[0].ext.prebid.targeting + def targeting = response.seatbid.bid.ext.prebid.targeting.flatten().collectEntries() assert targeting["hb_pb_${GENERIC}"] assert targeting["hb_size_${GENERIC}"] - assert targeting["hb_bidder"] == GENERIC.value assert targeting["hb_bidder_${GENERIC}"] == GENERIC.value + assert !targeting["hb_pb_${AMX}"] + assert !targeting["hb_size_${AMX}"] + assert !targeting["hb_bidder_${AMX}"] + and: "Response should contain repose millis with corresponding bidder" assert response.ext.responsetimemillis.containsKey(GENERIC.value) + assert !response.ext.responsetimemillis.containsKey(AMX.value) and: "Bidder request should be valid" assert bidder.getBidderRequests(bidRequest.id) @@ -1648,24 +1652,19 @@ class AlternateBidderCodeSpec extends BaseSpec { def response = pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest) then: "Bid response should contain seat" - assert response.seatbid.seat == [GENERIC, AMX] + assert response.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "Response should contain adapter code" assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX, AMX] and: "Response should contain bidder targeting" - def genericTargeting = response.seatbid[0].bid[0].ext.prebid.targeting - assert genericTargeting["hb_pb_${GENERIC}"] - assert genericTargeting["hb_size_${GENERIC}"] - assert genericTargeting["hb_bidder"] == GENERIC.value - assert genericTargeting["hb_bidder_${GENERIC}"] == GENERIC.value - - and: "Response should contain bidder targeting" - def amxTargeting = response.seatbid[1].bid[0].ext.prebid.targeting - assert amxTargeting["hb_pb_${AMX}"] - assert amxTargeting["hb_size_${AMX}"] - assert amxTargeting["hb_bidder"] == AMX.value - assert amxTargeting["hb_bidder_${AMX}"] == AMX.value + def targeting = response.seatbid.bid.ext.prebid.targeting.flatten().collectEntries() + assert targeting["hb_pb_${AMX}"] + assert targeting["hb_size_${AMX}"] + assert targeting["hb_bidder_${AMX}"] == AMX.value + assert targeting["hb_pb_${GENERIC}"] + assert targeting["hb_size_${GENERIC}"] + assert targeting["hb_bidder_${GENERIC}"] == GENERIC.value and: "Response should contain repose millis with corresponding bidder" assert response.ext.responsetimemillis.containsKey(GENERIC.value) From 02bd23916b1b97b95a4e0f82de678a768d401202 Mon Sep 17 00:00:00 2001 From: markiian Date: Sun, 6 Apr 2025 21:37:03 +0300 Subject: [PATCH 4/7] Update after review --- .../request/auction/AuctionEnvironment.groovy | 1 - .../tests/AlternateBidderCodeSpec.groovy | 130 ++++++++++++++---- .../tests/bidder/openx/OpenxSpec.groovy | 4 +- 3 files changed, 110 insertions(+), 25 deletions(-) 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 index f880b7bc634..649c539e794 100644 --- 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 @@ -1,7 +1,6 @@ package org.prebid.server.functional.model.request.auction import com.fasterxml.jackson.annotation.JsonValue -import org.prebid.server.functional.util.PBSUtils enum AuctionEnvironment { diff --git a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy index d86eaabbc12..b07f65199c8 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy @@ -5,9 +5,13 @@ import org.prebid.server.functional.model.config.AccountConfig import org.prebid.server.functional.model.config.AlternateBidderCodes import org.prebid.server.functional.model.config.BidderConfig import org.prebid.server.functional.model.db.Account +import org.prebid.server.functional.model.db.StoredImp +import org.prebid.server.functional.model.db.StoredResponse import org.prebid.server.functional.model.request.auction.Amx import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.request.auction.Imp +import org.prebid.server.functional.model.request.auction.PrebidStoredRequest +import org.prebid.server.functional.model.request.auction.StoredBidResponse import org.prebid.server.functional.model.request.auction.Targeting import org.prebid.server.functional.model.response.auction.BidExt import org.prebid.server.functional.model.response.auction.BidResponse @@ -1573,7 +1577,7 @@ class AlternateBidderCodeSpec extends BaseSpec { pbsServiceFactory.removeContainer(pbsConfig) } - def "PBS should do something when different bidder response with same seat"() { + def "PBS should throw error when different bidder response with same seat"() { given: "Default bid request with amx and generic bidder" def bidRequest = getBidRequestWithAmxBidderAndAlternateBidderCode().tap { imp[0].ext.prebid.bidder.generic = new Generic() @@ -1589,28 +1593,58 @@ class AlternateBidderCodeSpec extends BaseSpec { and: "Flash metrics" flushMetrics(pbsServiceWithAmxBidder) + when: "PBS processes auction request" + pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest) + + then: "Request should fail with error" + def exception = thrown(PrebidServerException) + assert exception.statusCode == 500 + assert exception.responseBody.contains("Duplicate key generic") + } + + def "PBS should return two seat when same bidder response with different bidder code"() { + given: "Default bid request with amx and generic bidder" + def bidRequest = getBidRequestWithAmxBidderAndAlternateBidderCode().tap { + imp[0].ext.prebid.bidder.amx = new Amx() + imp.add(Imp.getDefaultImpression()) + imp[1].ext.prebid.bidder.amx = new Amx() + imp[1].ext.prebid.bidder.generic = null + ext.prebid.alternateBidderCodes.bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC, AMX])] + } + + and: "Bid response with bidder code" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap { + it.seatbid[0].bid[0].ext = new BidExt(bidderCode: GENERIC) + it.seatbid[0].bid[1].ext = new BidExt(bidderCode: AMX) + } + bidder.setResponse(bidRequest.id, bidResponse) + + and: "Flash metrics" + flushMetrics(pbsServiceWithAmxBidder) + when: "PBS processes auction request" def response = pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest) then: "Bid response should contain seat" - assert response.seatbid.seat == [GENERIC, GENERIC] + assert response.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "Response should contain adapter code" - assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten().sort() == [AMX, GENERIC].sort() + assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX, AMX] - and: "Response should contain bidder targeting" + and: "Response should contain bidder amx targeting" def targeting = response.seatbid.bid.ext.prebid.targeting.flatten().collectEntries() + assert targeting["hb_pb_${AMX}"] + assert targeting["hb_size_${AMX}"] + assert targeting["hb_bidder_${AMX}"] == AMX.value + + and: 'Response targeting should contain generic' assert targeting["hb_pb_${GENERIC}"] assert targeting["hb_size_${GENERIC}"] assert targeting["hb_bidder_${GENERIC}"] == GENERIC.value - assert !targeting["hb_pb_${AMX}"] - assert !targeting["hb_size_${AMX}"] - assert !targeting["hb_bidder_${AMX}"] - and: "Response should contain repose millis with corresponding bidder" assert response.ext.responsetimemillis.containsKey(GENERIC.value) - assert !response.ext.responsetimemillis.containsKey(AMX.value) + assert response.ext.responsetimemillis.containsKey(AMX.value) and: "Bidder request should be valid" assert bidder.getBidderRequests(bidRequest.id) @@ -1626,22 +1660,25 @@ class AlternateBidderCodeSpec extends BaseSpec { and: "PBS shouldn't emit validation metrics" def metrics = pbsServiceWithAmxBidder.sendCollectedMetricsRequest() assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(AMX)] + assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(GENERIC)] } - def "PBS should do something when same bidder response with different bidder code"() { - given: "Default bid request with amx and generic bidder" + def "PBS should populate seat bid from stored bid response when stored bid response and alternate bidder code specified"() { + given: "Default bid request with amx bidder" + def storedResponseId = PBSUtils.randomNumber def bidRequest = getBidRequestWithAmxBidderAndAlternateBidderCode().tap { - imp[0].ext.prebid.bidder.amx = new Amx() - imp.add(Imp.getDefaultImpression()) - imp[1].ext.prebid.bidder.amx = new Amx() - imp[1].ext.prebid.bidder.generic = null - ext.prebid.alternateBidderCodes.bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC, AMX])] + imp[0].ext.prebid.storedBidResponse = [new StoredBidResponse(id: storedResponseId, bidder: AMX)] + ext.prebid.alternateBidderCodes.bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC])] } + and: "Stored bid response in DB" + def storedBidResponse = BidResponse.getDefaultBidResponse(bidRequest) + def storedResponse = new StoredResponse(responseId: storedResponseId, storedBidResponse: storedBidResponse) + storedResponseDao.save(storedResponse) + and: "Bid response with bidder code" def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap { it.seatbid[0].bid[0].ext = new BidExt(bidderCode: GENERIC) - it.seatbid[0].bid[1].ext = new BidExt(bidderCode: AMX) } bidder.setResponse(bidRequest.id, bidResponse) @@ -1652,25 +1689,72 @@ class AlternateBidderCodeSpec extends BaseSpec { def response = pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest) then: "Bid response should contain seat" - assert response.seatbid.seat.sort() == [GENERIC, AMX].sort() + assert response.seatbid.seat.sort() == [AMX].sort() and: "Response should contain adapter code" - assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX, AMX] + assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX] - and: "Response should contain bidder targeting" + and: "Response should contain bidder generic targeting" def targeting = response.seatbid.bid.ext.prebid.targeting.flatten().collectEntries() assert targeting["hb_pb_${AMX}"] assert targeting["hb_size_${AMX}"] assert targeting["hb_bidder_${AMX}"] == AMX.value + + and: "Response should contain repose millis with corresponding bidder" + assert response.ext.responsetimemillis.containsKey(AMX.value) + + and: "Bidder request shouldn't be called due to storedBidResponse" + assert !bidder.getBidderRequests(bidRequest.id) + + and: "Response shouldn't contain warnings and error and seatNonBid" + assert !response.ext?.warnings + assert !response.ext?.errors + assert !response.ext?.seatnonbid + + and: "Response shouldn't contain demand source" + assert !response.seatbid.first.bid.first.ext.prebid.meta.demandSource + + and: "PBS shouldn't emit validation metrics" + def metrics = pbsServiceWithAmxBidder.sendCollectedMetricsRequest() + assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(AMX)] + } + + def "PBS auction allow bidder code when imp stored request and allowed bidder code present"() { + given: "Default bid request" + def bidRequest = getBidRequestWithAmxBidderAndAlternateBidderCode().tap { + imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomString) + ext.prebid.alternateBidderCodes.bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC])] + } + + and: "Save storedImp into DB" + def storedImp = StoredImp.getStoredImp(bidRequest) + storedImpDao.save(storedImp) + + and: "Bid response with bidder code" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap { + it.seatbid[0].bid[0].ext = new BidExt(bidderCode: GENERIC) + } + bidder.setResponse(bidRequest.id, bidResponse) + + when: "Requesting PBS auction" + def response = pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seat" + assert response.seatbid.seat.sort() == [GENERIC].sort() + + and: "Response should contain adapter code" + assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX] + + and: "Response should contain bidder generic targeting" + def targeting = response.seatbid.bid.ext.prebid.targeting.flatten().collectEntries() assert targeting["hb_pb_${GENERIC}"] assert targeting["hb_size_${GENERIC}"] assert targeting["hb_bidder_${GENERIC}"] == GENERIC.value and: "Response should contain repose millis with corresponding bidder" assert response.ext.responsetimemillis.containsKey(GENERIC.value) - assert response.ext.responsetimemillis.containsKey(AMX.value) - and: "Bidder request should be valid" + and: "Bidder request should be called" assert bidder.getBidderRequests(bidRequest.id) and: "Response shouldn't contain warnings and error and seatNonBid" @@ -1683,8 +1767,8 @@ class AlternateBidderCodeSpec extends BaseSpec { and: "PBS shouldn't emit validation metrics" def metrics = pbsServiceWithAmxBidder.sendCollectedMetricsRequest() - assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(AMX)] assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(GENERIC)] + assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(AMX)] } private static Account getAccountWithAlternateBidderCode(BidRequest bidRequest) { 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 2c0141288e2..8e6d7f1a57d 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 @@ -28,6 +28,8 @@ 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.AuctionEnvironment.SERVER_ORCHESTRATED +import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.UNKNOWN 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 @@ -198,7 +200,7 @@ class OpenxSpec extends BaseSpec { assert interestGroupAuctionSeller.impId == impId assert interestGroupAuctionSeller.config assert interestGroupAuctionSeller.ext.bidder == bidResponse.seatbid[0].seat.value - assert interestGroupAuctionSeller.ext.adapter == OPENX.value + assert interestGroupAuctionSeller.ext.adapter == bidResponse.seatbid[0].seat.value } def "PBS shouldn't populate fledge config when bid response didn't return fledge config"() { From 35ed5c6741890e0e82da55ab04b0208d2e4c0b4b Mon Sep 17 00:00:00 2001 From: markiian Date: Mon, 7 Apr 2025 12:49:25 +0300 Subject: [PATCH 5/7] Update after review --- .../tests/AlternateBidderCodeSpec.groovy | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy index b07f65199c8..bf211d47d0c 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy @@ -1577,7 +1577,7 @@ class AlternateBidderCodeSpec extends BaseSpec { pbsServiceFactory.removeContainer(pbsConfig) } - def "PBS should throw error when different bidder response with same seat"() { + def "PBS should populate two seat bid when different bidder response with same seat"() { given: "Default bid request with amx and generic bidder" def bidRequest = getBidRequestWithAmxBidderAndAlternateBidderCode().tap { imp[0].ext.prebid.bidder.generic = new Generic() @@ -1594,12 +1594,46 @@ class AlternateBidderCodeSpec extends BaseSpec { flushMetrics(pbsServiceWithAmxBidder) when: "PBS processes auction request" - pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest) + def response = pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest) - then: "Request should fail with error" - def exception = thrown(PrebidServerException) - assert exception.statusCode == 500 - assert exception.responseBody.contains("Duplicate key generic") + then: "Bid response should contain seat" + assert response.seatbid.seat.sort() == [GENERIC, GENERIC].sort() + + and: "Response should contain adapter code" + assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX, GENERIC] + + and: "Response should contain bidder amx targeting" + def targeting = response.seatbid.bid.ext.prebid.targeting.flatten().collectEntries() + assert targeting["hb_pb_${GENERIC}"] + assert targeting["hb_size_${GENERIC}"] + assert targeting["hb_bidder_${GENERIC}"] == GENERIC.value + + and: 'Response targeting should contain generic' + assert targeting["hb_pb_${GENERIC}"] + assert targeting["hb_size_${GENERIC}"] + assert targeting["hb_bidder_${GENERIC}"] == GENERIC.value + + and: "Response should contain repose millis with corresponding bidder" + assert response.ext.responsetimemillis.containsKey(GENERIC.value) + + and: "Response should contain repose millis with amx bidder" + assert !response.ext.responsetimemillis.containsKey(AMX.value) + + and: "Bidder request should be valid" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Response shouldn't contain warnings and error and seatNonBid" + assert !response.ext?.warnings + assert !response.ext?.errors + assert !response.ext?.seatnonbid + + and: "Response shouldn't contain demand source" + assert !response.seatbid.first.bid.first.ext.prebid.meta.demandSource + + and: "PBS shouldn't emit validation metrics" + def metrics = pbsServiceWithAmxBidder.sendCollectedMetricsRequest() + assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(AMX)] + assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(GENERIC)] } def "PBS should return two seat when same bidder response with different bidder code"() { From b39f68b98adfeb76f9fd4f84f391c47642db79fc Mon Sep 17 00:00:00 2001 From: markiian Date: Mon, 7 Apr 2025 14:53:26 +0300 Subject: [PATCH 6/7] Update after review --- .../functional/tests/AlternateBidderCodeSpec.groovy | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy index bf211d47d0c..7a3604f1c15 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy @@ -1600,7 +1600,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.seatbid.seat.sort() == [GENERIC, GENERIC].sort() and: "Response should contain adapter code" - assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX, GENERIC] + assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten().sort() == [AMX, GENERIC].sort() and: "Response should contain bidder amx targeting" def targeting = response.seatbid.bid.ext.prebid.targeting.flatten().collectEntries() @@ -1608,15 +1608,10 @@ class AlternateBidderCodeSpec extends BaseSpec { assert targeting["hb_size_${GENERIC}"] assert targeting["hb_bidder_${GENERIC}"] == GENERIC.value - and: 'Response targeting should contain generic' - assert targeting["hb_pb_${GENERIC}"] - assert targeting["hb_size_${GENERIC}"] - assert targeting["hb_bidder_${GENERIC}"] == GENERIC.value - and: "Response should contain repose millis with corresponding bidder" assert response.ext.responsetimemillis.containsKey(GENERIC.value) - and: "Response should contain repose millis with amx bidder" + and: "Response shouldn't contain repose millis with amx bidder" assert !response.ext.responsetimemillis.containsKey(AMX.value) and: "Bidder request should be valid" @@ -1639,7 +1634,6 @@ class AlternateBidderCodeSpec extends BaseSpec { def "PBS should return two seat when same bidder response with different bidder code"() { given: "Default bid request with amx and generic bidder" def bidRequest = getBidRequestWithAmxBidderAndAlternateBidderCode().tap { - imp[0].ext.prebid.bidder.amx = new Amx() imp.add(Imp.getDefaultImpression()) imp[1].ext.prebid.bidder.amx = new Amx() imp[1].ext.prebid.bidder.generic = null @@ -1723,7 +1717,7 @@ class AlternateBidderCodeSpec extends BaseSpec { def response = pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest) then: "Bid response should contain seat" - assert response.seatbid.seat.sort() == [AMX].sort() + assert response.seatbid.seat == [AMX] and: "Response should contain adapter code" assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX] From 324a8085563dd011038483d61259bedc5fa2d52e Mon Sep 17 00:00:00 2001 From: Markiyan Mykush <95693607+marki1an@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:19:53 +0300 Subject: [PATCH 7/7] Minor update --- .../server/functional/tests/AlternateBidderCodeSpec.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy index 7a3604f1c15..f47efd346f8 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy @@ -1602,7 +1602,7 @@ class AlternateBidderCodeSpec extends BaseSpec { and: "Response should contain adapter code" assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten().sort() == [AMX, GENERIC].sort() - and: "Response should contain bidder amx targeting" + and: "Response should contain bidder generic targeting" def targeting = response.seatbid.bid.ext.prebid.targeting.flatten().collectEntries() assert targeting["hb_pb_${GENERIC}"] assert targeting["hb_size_${GENERIC}"]