Skip to content

Commit a828c32

Browse files
authored
Test: bid adjustments matching strategy (#3940)
1 parent 73e2085 commit a828c32

2 files changed

Lines changed: 330 additions & 3 deletions

File tree

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
package org.prebid.server.functional.model.request.auction
22

33
import com.fasterxml.jackson.annotation.JsonProperty
4-
import com.fasterxml.jackson.databind.PropertyNamingStrategies
5-
import com.fasterxml.jackson.databind.annotation.JsonNaming
64
import groovy.transform.ToString
75

8-
@JsonNaming(PropertyNamingStrategies.LowerCaseStrategy)
96
@ToString(includeNames = true, ignoreNulls = true)
107
class BidAdjustmentRule {
118

129
@JsonProperty('*')
1310
Map<String, List<AdjustmentRule>> wildcardBidder
1411
Map<String, List<AdjustmentRule>> generic
1512
Map<String, List<AdjustmentRule>> alias
13+
@JsonProperty("ALIAS")
14+
Map<String, List<AdjustmentRule>> aliasUpperCase
15+
@JsonProperty("AlIaS")
16+
Map<String, List<AdjustmentRule>> aliasCamelCase
17+
Map<String, List<AdjustmentRule>> amx
1618
}

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

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST
3232
import static org.prebid.server.functional.model.Currency.EUR
3333
import static org.prebid.server.functional.model.Currency.GBP
3434
import static org.prebid.server.functional.model.Currency.USD
35+
import static org.prebid.server.functional.model.bidder.BidderName.ACUITYADS
3536
import static org.prebid.server.functional.model.bidder.BidderName.ALIAS
3637
import static org.prebid.server.functional.model.bidder.BidderName.AMX
3738
import static org.prebid.server.functional.model.bidder.BidderName.APPNEXUS
@@ -79,6 +80,11 @@ class BidAdjustmentSpec extends BaseSpec {
7980
"adapters.amx.endpoint": "$networkServiceContainer.rootUri/auction".toString()]
8081
private static final PrebidServerService pbsService = pbsServiceFactory.getService(externalCurrencyConverterConfig + AMX_CONFIG)
8182

83+
@Override
84+
def cleanupSpec() {
85+
pbsServiceFactory.removeContainer(externalCurrencyConverterConfig + AMX_CONFIG)
86+
}
87+
8288
def "PBS should adjust bid price for matching bidder when request has per-bidder bid adjustment factors"() {
8389
given: "Default bid request with bid adjustment"
8490
def bidRequest = BidRequest.getDefaultBidRequest(SITE).tap {
@@ -1193,6 +1199,325 @@ class BidAdjustmentSpec extends BaseSpec {
11931199
bidAdjustmentFactor << [0.9, 1.1]
11941200
}
11951201

1202+
def "PBS shouldn't adjust bid price when bid adjustment rule doesn't match with bidder code"() {
1203+
given: "Bid request with ext.prebid.bidAdjustments and ext.prebid.alternateBidderCode"
1204+
def exactRulePrice = PBSUtils.randomPrice
1205+
def currency = USD
1206+
def adjustmentRule = new AdjustmentRule(adjustmentType: STATIC, value: exactRulePrice, currency: currency)
1207+
def bidAdjustmentRule = new BidAdjustmentRule((bidAdjustmentRuleBidder): [(WILDCARD): [adjustmentRule]])
1208+
def bidRequest = BidRequest.defaultBidRequest.tap {
1209+
cur = [currency]
1210+
imp[0].ext.prebid.bidder.generic = null
1211+
imp[0].ext.prebid.bidder.amx = new Amx()
1212+
ext.prebid.tap {
1213+
bidAdjustments = new BidAdjustment(mediaType: [(BANNER): bidAdjustmentRule])
1214+
alternateBidderCodes = new AlternateBidderCodes().tap {
1215+
enabled = true
1216+
bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [AMX])]
1217+
}
1218+
}
1219+
}
1220+
1221+
and: "Default bid response with price and bidder code"
1222+
def originalPrice = PBSUtils.randomPrice
1223+
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap {
1224+
cur = currency
1225+
seatbid.first.bid.first.price = originalPrice
1226+
seatbid.first.bid.first.ext = new BidExt(bidderCode: ACUITYADS)
1227+
}
1228+
bidder.setResponse(bidRequest.id, bidResponse)
1229+
1230+
when: "PBS processes auction request"
1231+
def response = pbsService.sendAuctionRequest(bidRequest)
1232+
1233+
then: "Final bid price should be adjusted according to exact rule"
1234+
assert response.seatbid.first.bid.first.price == originalPrice
1235+
assert response.cur == bidResponse.cur
1236+
1237+
and: "Original bid price and currency should be presented in bid.ext"
1238+
verifyAll(response.seatbid.first.bid.first.ext) {
1239+
origbidcpm == originalPrice
1240+
origbidcur == bidResponse.cur
1241+
}
1242+
1243+
and: "Bidder request should contain currency from request"
1244+
def bidderRequest = bidder.getBidderRequest(bidRequest.id)
1245+
assert bidderRequest.cur == [currency]
1246+
1247+
and: "Response should contain adapter code"
1248+
assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX]
1249+
1250+
and: "Response should contain seatbid.seat"
1251+
assert response.seatbid[0].seat == ACUITYADS
1252+
1253+
where:
1254+
bidAdjustmentRuleBidder << ["alias", "aliasUpperCase", "aliasCamelCase"]
1255+
}
1256+
1257+
def "PBS should adjust bid price when two bid adjustment rules are compatible"() {
1258+
given: "Bid request with ext.prebid.bidAdjustments and ext.prebid.alternateBidderCode"
1259+
def exactRulePrice = PBSUtils.randomPrice
1260+
def currency = USD
1261+
def dealId = PBSUtils.randomString
1262+
def adjustmentRule = new AdjustmentRule(adjustmentType: STATIC, value: exactRulePrice, currency: currency)
1263+
def firstBidAdjustmentRule = new BidAdjustmentRule(amx: [(dealId): [adjustmentRule]])
1264+
def secondBidAdjustmentRule = new BidAdjustmentRule(amx: [(WILDCARD): [adjustmentRule]])
1265+
def bidRequest = BidRequest.defaultBidRequest.tap {
1266+
cur = [currency]
1267+
imp[0].ext.prebid.bidder.generic = null
1268+
imp[0].ext.prebid.bidder.amx = new Amx()
1269+
ext.prebid.tap {
1270+
bidAdjustments = new BidAdjustment(mediaType: [(BANNER): firstBidAdjustmentRule,
1271+
(ANY) : secondBidAdjustmentRule])
1272+
alternateBidderCodes = new AlternateBidderCodes().tap {
1273+
enabled = true
1274+
bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [AMX])]
1275+
}
1276+
}
1277+
}
1278+
1279+
and: "Default bid response with price and bidder code and dealId"
1280+
def originalPrice = PBSUtils.randomPrice
1281+
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap {
1282+
cur = currency
1283+
seatbid.first.bid.first.price = originalPrice
1284+
seatbid.first.bid.first.ext = new BidExt(bidderCode: AMX)
1285+
seatbid.first.bid.first.dealid = dealId
1286+
}
1287+
bidder.setResponse(bidRequest.id, bidResponse)
1288+
1289+
when: "PBS processes auction request"
1290+
def response = pbsService.sendAuctionRequest(bidRequest)
1291+
1292+
then: "Final bid price should be adjusted according to exact rule"
1293+
assert response.seatbid.first.bid.first.price == getAdjustedPrice(originalPrice, exactRulePrice, STATIC)
1294+
assert response.cur == bidResponse.cur
1295+
1296+
and: "Original bid price and currency should be presented in bid.ext"
1297+
verifyAll(response.seatbid.first.bid.first.ext) {
1298+
origbidcpm == originalPrice
1299+
origbidcur == bidResponse.cur
1300+
}
1301+
1302+
and: "Bidder request should contain currency from request"
1303+
def bidderRequest = bidder.getBidderRequest(bidRequest.id)
1304+
assert bidderRequest.cur == [currency]
1305+
1306+
and: "Response should contain adapter code"
1307+
assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX]
1308+
1309+
and: "Response should contain seatbid.seat"
1310+
assert response.seatbid[0].seat == AMX
1311+
}
1312+
1313+
def "PBS should adjust bid price when bid adjustment bidder and bidder code different"() {
1314+
given: "Bid request with ext.prebid.bidAdjustments and ext.prebid.alternateBidderCode"
1315+
def exactRulePrice = PBSUtils.randomPrice
1316+
def currency = USD
1317+
def adjustmentRule = new AdjustmentRule(adjustmentType: STATIC, value: exactRulePrice, currency: currency)
1318+
def bidAdjustmentRule = new BidAdjustmentRule((bidAdjustmentRuleBidder): [(WILDCARD): [adjustmentRule]])
1319+
def bidRequest = BidRequest.defaultBidRequest.tap {
1320+
cur = [currency]
1321+
imp[0].ext.prebid.bidder.generic = null
1322+
imp[0].ext.prebid.bidder.amx = new Amx()
1323+
ext.prebid.tap {
1324+
bidAdjustments = new BidAdjustment(mediaType: [(BANNER): bidAdjustmentRule])
1325+
alternateBidderCodes = new AlternateBidderCodes().tap {
1326+
enabled = true
1327+
bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [AMX])]
1328+
}
1329+
}
1330+
}
1331+
1332+
and: "Default bid response with price and bidder code"
1333+
def originalPrice = PBSUtils.randomPrice
1334+
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap {
1335+
cur = currency
1336+
seatbid.first.bid.first.price = originalPrice
1337+
seatbid.first.bid.first.ext = new BidExt(bidderCode: ALIAS)
1338+
}
1339+
bidder.setResponse(bidRequest.id, bidResponse)
1340+
1341+
when: "PBS processes auction request"
1342+
def response = pbsService.sendAuctionRequest(bidRequest)
1343+
1344+
then: "Final bid price should be adjusted according to exact rule"
1345+
assert response.seatbid.first.bid.first.price == getAdjustedPrice(originalPrice, exactRulePrice, STATIC)
1346+
assert response.cur == bidResponse.cur
1347+
1348+
and: "Original bid price and currency should be presented in bid.ext"
1349+
verifyAll(response.seatbid.first.bid.first.ext) {
1350+
origbidcpm == originalPrice
1351+
origbidcur == bidResponse.cur
1352+
}
1353+
1354+
and: "Bidder request should contain currency from request"
1355+
def bidderRequest = bidder.getBidderRequest(bidRequest.id)
1356+
assert bidderRequest.cur == [currency]
1357+
1358+
and: "Response should contain adapter code"
1359+
assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX]
1360+
1361+
and: "Response should contain seatbid.seat"
1362+
assert response.seatbid[0].seat == ALIAS
1363+
1364+
where:
1365+
bidAdjustmentRuleBidder << ["alias", "aliasUpperCase", "aliasCamelCase"]
1366+
}
1367+
1368+
def "PBS should adjust bid price when bid adjustment bidder and bidder code same as requested"() {
1369+
given: "Bid request with ext.prebid.bidAdjustments and ext.prebid.alternateBidderCode"
1370+
def exactRulePrice = PBSUtils.randomPrice
1371+
def currency = USD
1372+
def exactRule = new BidAdjustmentRule(amx: [(WILDCARD): [new AdjustmentRule(adjustmentType: STATIC, value: exactRulePrice, currency: currency)]])
1373+
def bidRequest = BidRequest.defaultBidRequest.tap {
1374+
cur = [currency]
1375+
imp[0].ext.prebid.bidder.generic = null
1376+
imp[0].ext.prebid.bidder.amx = new Amx()
1377+
ext.prebid.tap {
1378+
bidAdjustments = new BidAdjustment(mediaType: [(BANNER): exactRule])
1379+
alternateBidderCodes = new AlternateBidderCodes().tap {
1380+
enabled = true
1381+
bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [AMX])]
1382+
}
1383+
}
1384+
}
1385+
1386+
and: "Default bid response with price and bidder code"
1387+
def originalPrice = PBSUtils.randomPrice
1388+
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap {
1389+
cur = currency
1390+
seatbid.first.bid.first.price = originalPrice
1391+
seatbid.first.bid.first.ext = new BidExt(bidderCode: AMX)
1392+
}
1393+
bidder.setResponse(bidRequest.id, bidResponse)
1394+
1395+
when: "PBS processes auction request"
1396+
def response = pbsService.sendAuctionRequest(bidRequest)
1397+
1398+
then: "Final bid price should be adjusted according to exact rule"
1399+
assert response.seatbid.first.bid.first.price == getAdjustedPrice(originalPrice, exactRulePrice, STATIC)
1400+
assert response.cur == bidResponse.cur
1401+
1402+
and: "Original bid price and currency should be presented in bid.ext"
1403+
verifyAll(response.seatbid.first.bid.first.ext) {
1404+
origbidcpm == originalPrice
1405+
origbidcur == bidResponse.cur
1406+
}
1407+
1408+
and: "Bidder request should contain currency from request"
1409+
def bidderRequest = bidder.getBidderRequest(bidRequest.id)
1410+
assert bidderRequest.cur == [currency]
1411+
1412+
and: "Response should contain adapter code"
1413+
assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX]
1414+
1415+
and: "Response should contain seatbid.seat"
1416+
assert response.seatbid[0].seat == AMX
1417+
}
1418+
1419+
def "PBS should adjust bid price when bid adjustment bidder is the same as bidder code"() {
1420+
given: "Bid request with ext.prebid.bidAdjustments and ext.prebid.alternateBidderCode"
1421+
def exactRulePrice = PBSUtils.randomPrice
1422+
def currency = USD
1423+
def exactRule = new BidAdjustmentRule(alias: [(WILDCARD): [new AdjustmentRule(adjustmentType: STATIC, value: exactRulePrice, currency: currency)]])
1424+
def bidRequest = BidRequest.defaultBidRequest.tap {
1425+
cur = [currency]
1426+
imp[0].ext.prebid.bidder.generic = null
1427+
imp[0].ext.prebid.bidder.amx = new Amx()
1428+
ext.prebid.tap {
1429+
bidAdjustments = new BidAdjustment(mediaType: [(BANNER): exactRule])
1430+
alternateBidderCodes = new AlternateBidderCodes().tap {
1431+
enabled = true
1432+
bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [AMX])]
1433+
}
1434+
}
1435+
}
1436+
1437+
and: "Default bid response with price and bidder code"
1438+
def originalPrice = PBSUtils.randomPrice
1439+
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap {
1440+
cur = currency
1441+
seatbid.first.bid.first.price = originalPrice
1442+
seatbid.first.bid.first.ext = new BidExt(bidderCode: ALIAS)
1443+
}
1444+
bidder.setResponse(bidRequest.id, bidResponse)
1445+
1446+
when: "PBS processes auction request"
1447+
def response = pbsService.sendAuctionRequest(bidRequest)
1448+
1449+
then: "Final bid price should be adjusted according to exact rule"
1450+
assert response.seatbid.first.bid.first.price == getAdjustedPrice(originalPrice, exactRulePrice, STATIC)
1451+
assert response.cur == bidResponse.cur
1452+
1453+
and: "Original bid price and currency should be presented in bid.ext"
1454+
verifyAll(response.seatbid.first.bid.first.ext) {
1455+
origbidcpm == originalPrice
1456+
origbidcur == bidResponse.cur
1457+
}
1458+
1459+
and: "Bidder request should contain currency from request"
1460+
def bidderRequest = bidder.getBidderRequest(bidRequest.id)
1461+
assert bidderRequest.cur == [currency]
1462+
1463+
and: "Response should contain adapter code"
1464+
assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX]
1465+
1466+
and: "Response should contain seatbid.seat"
1467+
assert response.seatbid[0].seat == ALIAS
1468+
}
1469+
1470+
def "PBS should adjust bid price when bid adjustment wildcard bidder and bidder code specified"() {
1471+
given: "Bid request with ext.prebid.bidAdjustments and ext.prebid.alternateBidderCode"
1472+
def exactRulePrice = PBSUtils.randomPrice
1473+
def currency = USD
1474+
def exactRule = new BidAdjustmentRule(wildcardBidder: [(WILDCARD): [new AdjustmentRule(adjustmentType: STATIC, value: exactRulePrice, currency: currency)]])
1475+
def bidRequest = BidRequest.defaultBidRequest.tap {
1476+
cur = [currency]
1477+
imp[0].ext.prebid.bidder.generic = null
1478+
imp[0].ext.prebid.bidder.amx = new Amx()
1479+
ext.prebid.tap {
1480+
bidAdjustments = new BidAdjustment(mediaType: [(BANNER): exactRule])
1481+
alternateBidderCodes = new AlternateBidderCodes().tap {
1482+
enabled = true
1483+
bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [AMX])]
1484+
}
1485+
}
1486+
}
1487+
1488+
and: "Default bid response with price and bidder code"
1489+
def originalPrice = PBSUtils.randomPrice
1490+
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap {
1491+
cur = currency
1492+
seatbid.first.bid.first.price = originalPrice
1493+
seatbid.first.bid.first.ext = new BidExt(bidderCode: ALIAS)
1494+
}
1495+
bidder.setResponse(bidRequest.id, bidResponse)
1496+
1497+
when: "PBS processes auction request"
1498+
def response = pbsService.sendAuctionRequest(bidRequest)
1499+
1500+
then: "Final bid price should be adjusted according to exact rule"
1501+
assert response.seatbid.first.bid.first.price == getAdjustedPrice(originalPrice, exactRulePrice, STATIC)
1502+
assert response.cur == bidResponse.cur
1503+
1504+
and: "Original bid price and currency should be presented in bid.ext"
1505+
verifyAll(response.seatbid.first.bid.first.ext) {
1506+
origbidcpm == originalPrice
1507+
origbidcur == bidResponse.cur
1508+
}
1509+
1510+
and: "Bidder request should contain currency from request"
1511+
def bidderRequest = bidder.getBidderRequest(bidRequest.id)
1512+
assert bidderRequest.cur == [currency]
1513+
1514+
and: "Response should contain adapter code"
1515+
assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX]
1516+
1517+
and: "Response should contain seatbid.seat"
1518+
assert response.seatbid[0].seat == ALIAS
1519+
}
1520+
11961521
private static Map<String, String> getExternalCurrencyConverterConfig() {
11971522
["auction.ad-server-currency" : DEFAULT_CURRENCY as String,
11981523
"currency-converter.external-rates.enabled" : "true",

0 commit comments

Comments
 (0)