@@ -5,8 +5,13 @@ import org.prebid.server.functional.model.config.AccountConfig
55import org.prebid.server.functional.model.config.AlternateBidderCodes
66import org.prebid.server.functional.model.config.BidderConfig
77import 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
810import org.prebid.server.functional.model.request.auction.Amx
911import 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
1015import org.prebid.server.functional.model.request.auction.Targeting
1116import org.prebid.server.functional.model.response.auction.BidExt
1217import 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
0 commit comments