Skip to content

Commit 6039b4d

Browse files
authored
Test: Multiple bidder codes (#3866)
1 parent 789a9d9 commit 6039b4d

File tree

5 files changed

+351
-13
lines changed

5 files changed

+351
-13
lines changed

src/test/groovy/org/prebid/server/functional/model/request/auction/AuctionEnvironment.groovy

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.prebid.server.functional.model.request.auction
22

33
import com.fasterxml.jackson.annotation.JsonValue
4-
import org.prebid.server.functional.util.PBSUtils
54

65
enum AuctionEnvironment {
76

src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy

Lines changed: 234 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@ import org.prebid.server.functional.model.config.AccountConfig
55
import org.prebid.server.functional.model.config.AlternateBidderCodes
66
import org.prebid.server.functional.model.config.BidderConfig
77
import org.prebid.server.functional.model.db.Account
8+
import org.prebid.server.functional.model.db.StoredImp
9+
import org.prebid.server.functional.model.db.StoredResponse
810
import org.prebid.server.functional.model.request.auction.Amx
911
import org.prebid.server.functional.model.request.auction.BidRequest
12+
import org.prebid.server.functional.model.request.auction.Imp
13+
import org.prebid.server.functional.model.request.auction.PrebidStoredRequest
14+
import org.prebid.server.functional.model.request.auction.StoredBidResponse
1015
import org.prebid.server.functional.model.request.auction.Targeting
1116
import org.prebid.server.functional.model.response.auction.BidExt
1217
import org.prebid.server.functional.model.response.auction.BidResponse
@@ -269,7 +274,7 @@ class AlternateBidderCodeSpec extends BaseSpec {
269274
assert response.ext.seatnonbid.size() == 1
270275

271276
def seatNonBid = response.ext.seatnonbid[0]
272-
assert seatNonBid.seat == AMX.value
277+
assert seatNonBid.seat == bidderName.value
273278
assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id
274279
assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL
275280

@@ -324,7 +329,7 @@ class AlternateBidderCodeSpec extends BaseSpec {
324329
assert response.ext.seatnonbid.size() == 1
325330

326331
def seatNonBid = response.ext.seatnonbid[0]
327-
assert seatNonBid.seat == ALIAS.value
332+
assert seatNonBid.seat == bidderName.value
328333
assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id
329334
assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL
330335

@@ -782,7 +787,7 @@ class AlternateBidderCodeSpec extends BaseSpec {
782787
assert response.ext.seatnonbid.size() == 1
783788

784789
def seatNonBid = response.ext.seatnonbid[0]
785-
assert seatNonBid.seat == AMX.value
790+
assert seatNonBid.seat == UNKNOWN.value
786791
assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id
787792
assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL
788793

@@ -838,7 +843,7 @@ class AlternateBidderCodeSpec extends BaseSpec {
838843
assert response.ext.seatnonbid.size() == 1
839844

840845
def seatNonBid = response.ext.seatnonbid[0]
841-
assert seatNonBid.seat == AMX.value
846+
assert seatNonBid.seat == UNKNOWN.value
842847
assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id
843848
assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL
844849

@@ -1182,7 +1187,7 @@ class AlternateBidderCodeSpec extends BaseSpec {
11821187
assert response.ext.seatnonbid.size() == 1
11831188

11841189
def seatNonBid = response.ext.seatnonbid[0]
1185-
assert seatNonBid.seat == AMX.value
1190+
assert seatNonBid.seat == requestedAllowedBidderCode.value
11861191
assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id
11871192
assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL
11881193

@@ -1237,7 +1242,7 @@ class AlternateBidderCodeSpec extends BaseSpec {
12371242
assert response.ext.seatnonbid.size() == 1
12381243

12391244
def seatNonBid = response.ext.seatnonbid[0]
1240-
assert seatNonBid.seat == AMX.value
1245+
assert seatNonBid.seat == requestedAllowedBidderCode.value
12411246
assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id
12421247
assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL
12431248

@@ -1298,7 +1303,7 @@ class AlternateBidderCodeSpec extends BaseSpec {
12981303
assert response.ext.seatnonbid.size() == 1
12991304

13001305
def seatNonBid = response.ext.seatnonbid[0]
1301-
assert seatNonBid.seat == AMX.value
1306+
assert seatNonBid.seat == allowedBidderCodes.value
13021307
assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id
13031308
assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL
13041309

@@ -1572,6 +1577,228 @@ class AlternateBidderCodeSpec extends BaseSpec {
15721577
pbsServiceFactory.removeContainer(pbsConfig)
15731578
}
15741579

1580+
def "PBS should populate two seat bid when different bidder response with same seat"() {
1581+
given: "Default bid request with amx and generic bidder"
1582+
def bidRequest = getBidRequestWithAmxBidderAndAlternateBidderCode().tap {
1583+
imp[0].ext.prebid.bidder.generic = new Generic()
1584+
ext.prebid.alternateBidderCodes.bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC])]
1585+
}
1586+
1587+
and: "Bid response with bidder code"
1588+
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap {
1589+
it.seatbid[0].bid[0].ext = new BidExt(bidderCode: GENERIC)
1590+
}
1591+
bidder.setResponse(bidRequest.id, bidResponse)
1592+
1593+
and: "Flash metrics"
1594+
flushMetrics(pbsServiceWithAmxBidder)
1595+
1596+
when: "PBS processes auction request"
1597+
def response = pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest)
1598+
1599+
then: "Bid response should contain seat"
1600+
assert response.seatbid.seat.sort() == [GENERIC, GENERIC].sort()
1601+
1602+
and: "Response should contain adapter code"
1603+
assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten().sort() == [AMX, GENERIC].sort()
1604+
1605+
and: "Response should contain bidder generic targeting"
1606+
def targeting = response.seatbid.bid.ext.prebid.targeting.flatten().collectEntries()
1607+
assert targeting["hb_pb_${GENERIC}"]
1608+
assert targeting["hb_size_${GENERIC}"]
1609+
assert targeting["hb_bidder_${GENERIC}"] == GENERIC.value
1610+
1611+
and: "Response should contain repose millis with corresponding bidder"
1612+
assert response.ext.responsetimemillis.containsKey(GENERIC.value)
1613+
1614+
and: "Response shouldn't contain repose millis with amx bidder"
1615+
assert !response.ext.responsetimemillis.containsKey(AMX.value)
1616+
1617+
and: "Bidder request should be valid"
1618+
assert bidder.getBidderRequests(bidRequest.id)
1619+
1620+
and: "Response shouldn't contain warnings and error and seatNonBid"
1621+
assert !response.ext?.warnings
1622+
assert !response.ext?.errors
1623+
assert !response.ext?.seatnonbid
1624+
1625+
and: "Response shouldn't contain demand source"
1626+
assert !response.seatbid.first.bid.first.ext.prebid.meta.demandSource
1627+
1628+
and: "PBS shouldn't emit validation metrics"
1629+
def metrics = pbsServiceWithAmxBidder.sendCollectedMetricsRequest()
1630+
assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(AMX)]
1631+
assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(GENERIC)]
1632+
}
1633+
1634+
def "PBS should return two seat when same bidder response with different bidder code"() {
1635+
given: "Default bid request with amx and generic bidder"
1636+
def bidRequest = getBidRequestWithAmxBidderAndAlternateBidderCode().tap {
1637+
imp.add(Imp.getDefaultImpression())
1638+
imp[1].ext.prebid.bidder.amx = new Amx()
1639+
imp[1].ext.prebid.bidder.generic = null
1640+
ext.prebid.alternateBidderCodes.bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC, AMX])]
1641+
}
1642+
1643+
and: "Bid response with bidder code"
1644+
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap {
1645+
it.seatbid[0].bid[0].ext = new BidExt(bidderCode: GENERIC)
1646+
it.seatbid[0].bid[1].ext = new BidExt(bidderCode: AMX)
1647+
}
1648+
bidder.setResponse(bidRequest.id, bidResponse)
1649+
1650+
and: "Flash metrics"
1651+
flushMetrics(pbsServiceWithAmxBidder)
1652+
1653+
when: "PBS processes auction request"
1654+
def response = pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest)
1655+
1656+
then: "Bid response should contain seat"
1657+
assert response.seatbid.seat.sort() == [GENERIC, AMX].sort()
1658+
1659+
and: "Response should contain adapter code"
1660+
assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX, AMX]
1661+
1662+
and: "Response should contain bidder amx targeting"
1663+
def targeting = response.seatbid.bid.ext.prebid.targeting.flatten().collectEntries()
1664+
assert targeting["hb_pb_${AMX}"]
1665+
assert targeting["hb_size_${AMX}"]
1666+
assert targeting["hb_bidder_${AMX}"] == AMX.value
1667+
1668+
and: 'Response targeting should contain generic'
1669+
assert targeting["hb_pb_${GENERIC}"]
1670+
assert targeting["hb_size_${GENERIC}"]
1671+
assert targeting["hb_bidder_${GENERIC}"] == GENERIC.value
1672+
1673+
and: "Response should contain repose millis with corresponding bidder"
1674+
assert response.ext.responsetimemillis.containsKey(GENERIC.value)
1675+
assert response.ext.responsetimemillis.containsKey(AMX.value)
1676+
1677+
and: "Bidder request should be valid"
1678+
assert bidder.getBidderRequests(bidRequest.id)
1679+
1680+
and: "Response shouldn't contain warnings and error and seatNonBid"
1681+
assert !response.ext?.warnings
1682+
assert !response.ext?.errors
1683+
assert !response.ext?.seatnonbid
1684+
1685+
and: "Response shouldn't contain demand source"
1686+
assert !response.seatbid.first.bid.first.ext.prebid.meta.demandSource
1687+
1688+
and: "PBS shouldn't emit validation metrics"
1689+
def metrics = pbsServiceWithAmxBidder.sendCollectedMetricsRequest()
1690+
assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(AMX)]
1691+
assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(GENERIC)]
1692+
}
1693+
1694+
def "PBS should populate seat bid from stored bid response when stored bid response and alternate bidder code specified"() {
1695+
given: "Default bid request with amx bidder"
1696+
def storedResponseId = PBSUtils.randomNumber
1697+
def bidRequest = getBidRequestWithAmxBidderAndAlternateBidderCode().tap {
1698+
imp[0].ext.prebid.storedBidResponse = [new StoredBidResponse(id: storedResponseId, bidder: AMX)]
1699+
ext.prebid.alternateBidderCodes.bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC])]
1700+
}
1701+
1702+
and: "Stored bid response in DB"
1703+
def storedBidResponse = BidResponse.getDefaultBidResponse(bidRequest)
1704+
def storedResponse = new StoredResponse(responseId: storedResponseId, storedBidResponse: storedBidResponse)
1705+
storedResponseDao.save(storedResponse)
1706+
1707+
and: "Bid response with bidder code"
1708+
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap {
1709+
it.seatbid[0].bid[0].ext = new BidExt(bidderCode: GENERIC)
1710+
}
1711+
bidder.setResponse(bidRequest.id, bidResponse)
1712+
1713+
and: "Flash metrics"
1714+
flushMetrics(pbsServiceWithAmxBidder)
1715+
1716+
when: "PBS processes auction request"
1717+
def response = pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest)
1718+
1719+
then: "Bid response should contain seat"
1720+
assert response.seatbid.seat == [AMX]
1721+
1722+
and: "Response should contain adapter code"
1723+
assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX]
1724+
1725+
and: "Response should contain bidder generic targeting"
1726+
def targeting = response.seatbid.bid.ext.prebid.targeting.flatten().collectEntries()
1727+
assert targeting["hb_pb_${AMX}"]
1728+
assert targeting["hb_size_${AMX}"]
1729+
assert targeting["hb_bidder_${AMX}"] == AMX.value
1730+
1731+
and: "Response should contain repose millis with corresponding bidder"
1732+
assert response.ext.responsetimemillis.containsKey(AMX.value)
1733+
1734+
and: "Bidder request shouldn't be called due to storedBidResponse"
1735+
assert !bidder.getBidderRequests(bidRequest.id)
1736+
1737+
and: "Response shouldn't contain warnings and error and seatNonBid"
1738+
assert !response.ext?.warnings
1739+
assert !response.ext?.errors
1740+
assert !response.ext?.seatnonbid
1741+
1742+
and: "Response shouldn't contain demand source"
1743+
assert !response.seatbid.first.bid.first.ext.prebid.meta.demandSource
1744+
1745+
and: "PBS shouldn't emit validation metrics"
1746+
def metrics = pbsServiceWithAmxBidder.sendCollectedMetricsRequest()
1747+
assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(AMX)]
1748+
}
1749+
1750+
def "PBS auction allow bidder code when imp stored request and allowed bidder code present"() {
1751+
given: "Default bid request"
1752+
def bidRequest = getBidRequestWithAmxBidderAndAlternateBidderCode().tap {
1753+
imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomString)
1754+
ext.prebid.alternateBidderCodes.bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC])]
1755+
}
1756+
1757+
and: "Save storedImp into DB"
1758+
def storedImp = StoredImp.getStoredImp(bidRequest)
1759+
storedImpDao.save(storedImp)
1760+
1761+
and: "Bid response with bidder code"
1762+
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap {
1763+
it.seatbid[0].bid[0].ext = new BidExt(bidderCode: GENERIC)
1764+
}
1765+
bidder.setResponse(bidRequest.id, bidResponse)
1766+
1767+
when: "Requesting PBS auction"
1768+
def response = pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest)
1769+
1770+
then: "Bid response should contain seat"
1771+
assert response.seatbid.seat.sort() == [GENERIC].sort()
1772+
1773+
and: "Response should contain adapter code"
1774+
assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX]
1775+
1776+
and: "Response should contain bidder generic targeting"
1777+
def targeting = response.seatbid.bid.ext.prebid.targeting.flatten().collectEntries()
1778+
assert targeting["hb_pb_${GENERIC}"]
1779+
assert targeting["hb_size_${GENERIC}"]
1780+
assert targeting["hb_bidder_${GENERIC}"] == GENERIC.value
1781+
1782+
and: "Response should contain repose millis with corresponding bidder"
1783+
assert response.ext.responsetimemillis.containsKey(GENERIC.value)
1784+
1785+
and: "Bidder request should be called"
1786+
assert bidder.getBidderRequests(bidRequest.id)
1787+
1788+
and: "Response shouldn't contain warnings and error and seatNonBid"
1789+
assert !response.ext?.warnings
1790+
assert !response.ext?.errors
1791+
assert !response.ext?.seatnonbid
1792+
1793+
and: "Response shouldn't contain demand source"
1794+
assert !response.seatbid.first.bid.first.ext.prebid.meta.demandSource
1795+
1796+
and: "PBS shouldn't emit validation metrics"
1797+
def metrics = pbsServiceWithAmxBidder.sendCollectedMetricsRequest()
1798+
assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(GENERIC)]
1799+
assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(AMX)]
1800+
}
1801+
15751802
private static Account getAccountWithAlternateBidderCode(BidRequest bidRequest) {
15761803
new Account().tap {
15771804
it.uuid = bidRequest.accountId

src/test/groovy/org/prebid/server/functional/tests/BidAdjustmentSpec.groovy

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.prebid.server.functional.tests
22

33
import org.prebid.server.functional.model.Currency
4+
import org.prebid.server.functional.model.bidder.Generic
45
import org.prebid.server.functional.model.config.AccountAuctionConfig
56
import org.prebid.server.functional.model.config.AccountConfig
67
import org.prebid.server.functional.model.config.AlternateBidderCodes
@@ -31,6 +32,7 @@ import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST
3132
import static org.prebid.server.functional.model.Currency.EUR
3233
import static org.prebid.server.functional.model.Currency.GBP
3334
import static org.prebid.server.functional.model.Currency.USD
35+
import static org.prebid.server.functional.model.bidder.BidderName.ALIAS
3436
import static org.prebid.server.functional.model.bidder.BidderName.AMX
3537
import static org.prebid.server.functional.model.bidder.BidderName.APPNEXUS
3638
import static org.prebid.server.functional.model.bidder.BidderName.GENERIC
@@ -1152,6 +1154,45 @@ class BidAdjustmentSpec extends BaseSpec {
11521154
bidAdjustmentFactor << [0.9, 1.1]
11531155
}
11541156

1157+
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"() {
1158+
given: "Default bid request with bid adjustment"
1159+
def bidRequest = BidRequest.getDefaultBidRequest(SITE).tap {
1160+
ext.prebid.aliases = [(ALIAS.value): AMX]
1161+
imp[0].ext.prebid.bidder.generic = null
1162+
imp[0].ext.prebid.bidder.amx = null
1163+
imp[0].ext.prebid.bidder.alias = new Generic()
1164+
ext.prebid.tap {
1165+
bidAdjustmentFactors = new BidAdjustmentFactors().tap {
1166+
adjustments = [(GENERIC): randomDecimal]
1167+
mediaTypes = [(BANNER): [(GENERIC): bidAdjustmentFactor]]
1168+
}
1169+
alternateBidderCodes = new AlternateBidderCodes().tap {
1170+
enabled = true
1171+
bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC])]
1172+
}
1173+
}
1174+
}
1175+
1176+
and: "Bid response with bidder code"
1177+
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap {
1178+
it.seatbid[0].bid[0].ext = new BidExt(bidderCode: GENERIC)
1179+
}
1180+
bidder.setResponse(bidRequest.id, bidResponse)
1181+
1182+
when: "PBS processes auction request"
1183+
def response = pbsService.sendAuctionRequest(bidRequest)
1184+
1185+
then: "Final bid price should be adjusted"
1186+
assert response?.seatbid?.first?.bid?.first?.price == bidResponse.seatbid.first.bid.first.price *
1187+
bidAdjustmentFactor
1188+
1189+
and: "Response should contain repose millis with corresponding bidder"
1190+
assert response.ext.responsetimemillis.containsKey(GENERIC.value)
1191+
1192+
where:
1193+
bidAdjustmentFactor << [0.9, 1.1]
1194+
}
1195+
11551196
private static Map<String, String> getExternalCurrencyConverterConfig() {
11561197
["auction.ad-server-currency" : DEFAULT_CURRENCY as String,
11571198
"currency-converter.external-rates.enabled" : "true",

0 commit comments

Comments
 (0)