From 7a7ca380f194f7e13f855a693dfddd9a19f7d20d Mon Sep 17 00:00:00 2001 From: markiian Date: Thu, 10 Apr 2025 01:00:25 +0300 Subject: [PATCH 1/7] Add functional tests for bid rounding --- .../model/config/AccountAuctionConfig.groovy | 4 + .../model/request/auction/BidRounding.groovy | 6 + .../server/functional/tests/BaseSpec.groovy | 5 + .../functional/tests/BidRoundingSpec.groovy | 148 ++++++++++++++++++ 4 files changed, 163 insertions(+) create mode 100644 src/test/groovy/org/prebid/server/functional/model/request/auction/BidRounding.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy diff --git a/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy index 8dc9831e3fa..5fdd53ac481 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming import groovy.transform.ToString import org.prebid.server.functional.model.bidder.BidderName import org.prebid.server.functional.model.request.auction.BidAdjustment +import org.prebid.server.functional.model.request.auction.BidRounding import org.prebid.server.functional.model.request.auction.PaaFormat import org.prebid.server.functional.model.request.auction.Targeting import org.prebid.server.functional.model.response.auction.MediaType @@ -31,6 +32,7 @@ class AccountAuctionConfig { PrivacySandbox privacySandbox @JsonProperty("bidadjustments") BidAdjustment bidAdjustments + BidRounding bidRounding @JsonProperty("price_granularity") PriceGranularityType priceGranularitySnakeCase @@ -48,4 +50,6 @@ class AccountAuctionConfig { AccountBidValidationConfig bidValidationsSnakeCase @JsonProperty("price_floors") AccountPriceFloorsConfig priceFloorsSnakeCase + @JsonProperty("bid_rounding") + BidRounding bidRoundingSnakeCase } diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRounding.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRounding.groovy new file mode 100644 index 00000000000..19132edd8ca --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRounding.groovy @@ -0,0 +1,6 @@ +package org.prebid.server.functional.model.request.auction + +enum BidRounding { + + UP, DOWN, TRUE, TIMESPLIT +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/BaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/BaseSpec.groovy index 63ba2516ff4..3b8f354b237 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/BaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/BaseSpec.groovy @@ -20,6 +20,7 @@ import org.prebid.server.functional.util.PBSUtils import spock.lang.Specification import static java.math.RoundingMode.DOWN +import static java.math.RoundingMode.UP import static org.prebid.server.functional.testcontainers.Dependencies.networkServiceContainer import static org.prebid.server.functional.util.SystemProperties.DEFAULT_TIMEOUT @@ -84,6 +85,10 @@ abstract class BaseSpec extends Specification implements ObjectMapperWrapper { "${value.setScale(DEFAULT_TARGETING_PRECISION, DOWN)}0" } + protected static String getRoundedTargetingValueWithUpPrecision(BigDecimal value) { + "${value.setScale(DEFAULT_TARGETING_PRECISION, UP )}0" + } + protected static Map> getRequests(BidResponse bidResponse) { bidResponse.ext.debug.bidders.collectEntries { bidderName, bidderCalls -> collectRequestByBidderName(bidderName, bidderCalls) diff --git a/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy new file mode 100644 index 00000000000..43beabd7309 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy @@ -0,0 +1,148 @@ +package org.prebid.server.functional.tests + +import org.prebid.server.functional.model.config.AccountAuctionConfig +import org.prebid.server.functional.model.config.AccountConfig +import org.prebid.server.functional.model.db.Account +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.MultiBid +import org.prebid.server.functional.model.request.auction.Targeting +import org.prebid.server.functional.model.response.auction.Bid +import org.prebid.server.functional.model.response.auction.BidResponse +import org.prebid.server.functional.util.PBSUtils + +import static org.prebid.server.functional.model.AccountStatus.ACTIVE +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.request.auction.BidRounding.DOWN +import static org.prebid.server.functional.model.request.auction.BidRounding.TIMESPLIT +import static org.prebid.server.functional.model.request.auction.BidRounding.TRUE +import static org.prebid.server.functional.model.request.auction.BidRounding.UP + +class BidRoundingSpec extends BaseSpec { + + def "PBS should round bid value to the down when account bid rounding empty"() { + given: "Default bid request" + def bidRequest = BidRequest.getDefaultBidRequest().tap { + ext.prebid.targeting = new Targeting() + } + + and: "Account in the DB" + def account = getAccountWithBidRounding(bidRequest.accountId, null) + accountDao.save(account) + + and: "Default bid response" + def bidPrice = PBSUtils.randomFloorValue + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { + seatbid[0].bid[0].price = bidPrice + } + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Price of bid response should be round" + def targeting = response.seatbid[0].bid[0].ext.prebid.targeting + assert targeting["hb_pb"] == getRoundedTargetingValueWithDefaultPrecision(bidPrice) + } + + def "PBS should round bid value to the up when account bid rounding UP or TRUE"() { + given: "Default bid request" + def bidRequest = BidRequest.getDefaultBidRequest().tap { + ext.prebid.targeting = new Targeting() + } + + and: "Account in the DB" + def account = getAccountWithBidRounding(bidRequest.accountId, accountAuctionConfig) + accountDao.save(account) + + and: "Default bid response" + def bidPrice = PBSUtils.getRandomFloorValue(0.15, 0.19) + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { + seatbid[0].bid[0].price = bidPrice + } + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Price of bid response should be round to the lower price" + def targeting = response.seatbid[0].bid[0].ext.prebid.targeting + assert targeting["hb_pb"] == getRoundedTargetingValueWithUpPrecision(bidPrice) + + where: + accountAuctionConfig << [new AccountAuctionConfig(bidRounding: UP), + new AccountAuctionConfig(bidRounding: UP), + new AccountAuctionConfig(bidRoundingSnakeCase: TRUE), + new AccountAuctionConfig(bidRoundingSnakeCase: TRUE)] + } + + def "PBS should round bid value to the down when account bid rounding DOWN or TRUE"() { + given: "Default bid request" + def bidRequest = BidRequest.getDefaultBidRequest().tap { + ext.prebid.targeting = new Targeting() + } + + and: "Account in the DB" + def account = getAccountWithBidRounding(bidRequest.accountId, accountAuctionConfig) + accountDao.save(account) + + and: "Default bid response" + def bidPrice = PBSUtils.getRandomFloorValue(0.11, 0.14) + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { + seatbid[0].bid[0].price = bidPrice + } + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Price of bid response should be round to the lower price" + def targeting = response.seatbid[0].bid[0].ext.prebid.targeting + assert targeting["hb_pb"] == getRoundedTargetingValueWithDefaultPrecision(bidPrice) + + where: + accountAuctionConfig << [new AccountAuctionConfig(bidRounding: DOWN), + new AccountAuctionConfig(bidRounding: DOWN), + new AccountAuctionConfig(bidRoundingSnakeCase: TRUE), + new AccountAuctionConfig(bidRoundingSnakeCase: TRUE)] + } + + def "PBS should round bid value to the 50% down and 50% up when account bid rounding time split"() { + given: "Default bid request" + def bidRequest = BidRequest.getDefaultBidRequest().tap { + ext.prebid.targeting = new Targeting(includeBidderKeys: true) + ext.prebid.multibid = [new MultiBid(bidder: GENERIC, maxBids: 2, targetBidderCodePrefix: GENERIC.value)] + } + + and: "Account in the DB" + def account = getAccountWithBidRounding(bidRequest.accountId, accountAuctionConfig) + accountDao.save(account) + + and: "Default bid response" + def bidPrice = PBSUtils.randomFloorValue + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { + seatbid[0].bid[0].price = bidPrice + seatbid[0].bid.add(Bid.getDefaultBids(bidRequest.imp)[0]) + seatbid[0].bid[1].price = bidPrice + } + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Price of bid response should be round" + def targeting = response.seatbid[0].bid.ext.prebid.targeting + assert targeting.collectEntries().findAll { it -> it.key.toString().contains("hb_pb_") } + .values().sort() == [getRoundedTargetingValueWithDefaultPrecision(bidPrice), + getRoundedTargetingValueWithUpPrecision(bidPrice)].sort() + + where: + accountAuctionConfig << [new AccountAuctionConfig(bidRounding: TIMESPLIT), + new AccountAuctionConfig(bidRoundingSnakeCase: TIMESPLIT)] + } + + + private static final Account getAccountWithBidRounding(String accountId, AccountAuctionConfig accountAuctionConfig) { + def accountConfig = new AccountConfig(status: ACTIVE, auction: accountAuctionConfig) + new Account(uuid: accountId, config: accountConfig) + } +} From 72da33abedd42b8e52caa5c2eb76adcabbb7f9e1 Mon Sep 17 00:00:00 2001 From: markiian Date: Thu, 10 Apr 2025 01:01:36 +0300 Subject: [PATCH 2/7] Minor update --- .../server/functional/tests/BidRoundingSpec.groovy | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy index 43beabd7309..141f171e934 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy @@ -39,7 +39,7 @@ class BidRoundingSpec extends BaseSpec { when: "PBS processes auction request" def response = defaultPbsService.sendAuctionRequest(bidRequest) - then: "Price of bid response should be round" + then: "Targeting hb_pb should be round" def targeting = response.seatbid[0].bid[0].ext.prebid.targeting assert targeting["hb_pb"] == getRoundedTargetingValueWithDefaultPrecision(bidPrice) } @@ -64,7 +64,7 @@ class BidRoundingSpec extends BaseSpec { when: "PBS processes auction request" def response = defaultPbsService.sendAuctionRequest(bidRequest) - then: "Price of bid response should be round to the lower price" + then: "Targeting hb_pb should be round" def targeting = response.seatbid[0].bid[0].ext.prebid.targeting assert targeting["hb_pb"] == getRoundedTargetingValueWithUpPrecision(bidPrice) @@ -95,7 +95,7 @@ class BidRoundingSpec extends BaseSpec { when: "PBS processes auction request" def response = defaultPbsService.sendAuctionRequest(bidRequest) - then: "Price of bid response should be round to the lower price" + then: "Targeting hb_pb should be round" def targeting = response.seatbid[0].bid[0].ext.prebid.targeting assert targeting["hb_pb"] == getRoundedTargetingValueWithDefaultPrecision(bidPrice) @@ -129,7 +129,7 @@ class BidRoundingSpec extends BaseSpec { when: "PBS processes auction request" def response = defaultPbsService.sendAuctionRequest(bidRequest) - then: "Price of bid response should be round" + then: "Targeting hb_pb should be round" def targeting = response.seatbid[0].bid.ext.prebid.targeting assert targeting.collectEntries().findAll { it -> it.key.toString().contains("hb_pb_") } .values().sort() == [getRoundedTargetingValueWithDefaultPrecision(bidPrice), @@ -140,7 +140,6 @@ class BidRoundingSpec extends BaseSpec { new AccountAuctionConfig(bidRoundingSnakeCase: TIMESPLIT)] } - private static final Account getAccountWithBidRounding(String accountId, AccountAuctionConfig accountAuctionConfig) { def accountConfig = new AccountConfig(status: ACTIVE, auction: accountAuctionConfig) new Account(uuid: accountId, config: accountConfig) From 605624442fbd80253c4c7cea394c9a05a8da7b57 Mon Sep 17 00:00:00 2001 From: markiian Date: Thu, 15 May 2025 13:05:19 +0300 Subject: [PATCH 3/7] Minor update --- .../model/request/auction/BidRounding.groovy | 20 ++++++++++++++++++- .../functional/tests/BidRoundingSpec.groovy | 20 +++++++++++-------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRounding.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRounding.groovy index 19132edd8ca..ba612ce9f87 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRounding.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/BidRounding.groovy @@ -1,6 +1,24 @@ package org.prebid.server.functional.model.request.auction +import com.fasterxml.jackson.annotation.JsonValue + enum BidRounding { - UP, DOWN, TRUE, TIMESPLIT + UP("up"), + DOWN("down"), + TRUE("true"), + TIME_SPLIT("timesplit"), + UNKNOWN("unknown"), + + private String value + + BidRounding(String value) { + this.value = value + } + + @Override + @JsonValue + String toString() { + return value + } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy index 141f171e934..2fc437ef4a0 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy @@ -13,20 +13,20 @@ import org.prebid.server.functional.util.PBSUtils import static org.prebid.server.functional.model.AccountStatus.ACTIVE import static org.prebid.server.functional.model.bidder.BidderName.GENERIC import static org.prebid.server.functional.model.request.auction.BidRounding.DOWN -import static org.prebid.server.functional.model.request.auction.BidRounding.TIMESPLIT import static org.prebid.server.functional.model.request.auction.BidRounding.TRUE +import static org.prebid.server.functional.model.request.auction.BidRounding.UNKNOWN import static org.prebid.server.functional.model.request.auction.BidRounding.UP class BidRoundingSpec extends BaseSpec { - def "PBS should round bid value to the down when account bid rounding empty"() { + def "PBS should round bid value to the down when account bid rounding empty or unknown"() { given: "Default bid request" def bidRequest = BidRequest.getDefaultBidRequest().tap { ext.prebid.targeting = new Targeting() } and: "Account in the DB" - def account = getAccountWithBidRounding(bidRequest.accountId, null) + def account = getAccountWithBidRounding(bidRequest.accountId, bidRoundingValue) accountDao.save(account) and: "Default bid response" @@ -42,6 +42,10 @@ class BidRoundingSpec extends BaseSpec { then: "Targeting hb_pb should be round" def targeting = response.seatbid[0].bid[0].ext.prebid.targeting assert targeting["hb_pb"] == getRoundedTargetingValueWithDefaultPrecision(bidPrice) + + where: + bidRoundingValue << [new AccountAuctionConfig(bidRounding: null), + new AccountAuctionConfig(bidRounding: UNKNOWN)] } def "PBS should round bid value to the up when account bid rounding UP or TRUE"() { @@ -70,8 +74,8 @@ class BidRoundingSpec extends BaseSpec { where: accountAuctionConfig << [new AccountAuctionConfig(bidRounding: UP), - new AccountAuctionConfig(bidRounding: UP), - new AccountAuctionConfig(bidRoundingSnakeCase: TRUE), + new AccountAuctionConfig(bidRounding: TRUE), + new AccountAuctionConfig(bidRoundingSnakeCase: UP), new AccountAuctionConfig(bidRoundingSnakeCase: TRUE)] } @@ -101,8 +105,8 @@ class BidRoundingSpec extends BaseSpec { where: accountAuctionConfig << [new AccountAuctionConfig(bidRounding: DOWN), - new AccountAuctionConfig(bidRounding: DOWN), - new AccountAuctionConfig(bidRoundingSnakeCase: TRUE), + new AccountAuctionConfig(bidRounding: TRUE), + new AccountAuctionConfig(bidRoundingSnakeCase: DOWN), new AccountAuctionConfig(bidRoundingSnakeCase: TRUE)] } @@ -118,7 +122,7 @@ class BidRoundingSpec extends BaseSpec { accountDao.save(account) and: "Default bid response" - def bidPrice = PBSUtils.randomFloorValue + def bidPrice = 0.15 def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { seatbid[0].bid[0].price = bidPrice seatbid[0].bid.add(Bid.getDefaultBids(bidRequest.imp)[0]) From 608c16b5483d7d43ee67308ed161d0ff9f6cf74a Mon Sep 17 00:00:00 2001 From: markiian Date: Thu, 15 May 2025 13:31:28 +0300 Subject: [PATCH 4/7] Update naming --- .../prebid/server/functional/tests/BidRoundingSpec.groovy | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy index 2fc437ef4a0..86f68190168 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy @@ -13,6 +13,7 @@ import org.prebid.server.functional.util.PBSUtils import static org.prebid.server.functional.model.AccountStatus.ACTIVE import static org.prebid.server.functional.model.bidder.BidderName.GENERIC import static org.prebid.server.functional.model.request.auction.BidRounding.DOWN +import static org.prebid.server.functional.model.request.auction.BidRounding.TIME_SPLIT import static org.prebid.server.functional.model.request.auction.BidRounding.TRUE import static org.prebid.server.functional.model.request.auction.BidRounding.UNKNOWN import static org.prebid.server.functional.model.request.auction.BidRounding.UP @@ -140,8 +141,8 @@ class BidRoundingSpec extends BaseSpec { getRoundedTargetingValueWithUpPrecision(bidPrice)].sort() where: - accountAuctionConfig << [new AccountAuctionConfig(bidRounding: TIMESPLIT), - new AccountAuctionConfig(bidRoundingSnakeCase: TIMESPLIT)] + accountAuctionConfig << [new AccountAuctionConfig(bidRounding: TIME_SPLIT), + new AccountAuctionConfig(bidRoundingSnakeCase: TIME_SPLIT)] } private static final Account getAccountWithBidRounding(String accountId, AccountAuctionConfig accountAuctionConfig) { From c1cbfafb2fac0865b05d3f71d9d5e9b1a544a2a7 Mon Sep 17 00:00:00 2001 From: markiian Date: Thu, 15 May 2025 14:41:03 +0300 Subject: [PATCH 5/7] Few update --- .../server/functional/tests/BidRoundingSpec.groovy | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy index 86f68190168..1fb6739ce40 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy @@ -6,7 +6,6 @@ import org.prebid.server.functional.model.db.Account import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.request.auction.MultiBid import org.prebid.server.functional.model.request.auction.Targeting -import org.prebid.server.functional.model.response.auction.Bid import org.prebid.server.functional.model.response.auction.BidResponse import org.prebid.server.functional.util.PBSUtils @@ -123,11 +122,9 @@ class BidRoundingSpec extends BaseSpec { accountDao.save(account) and: "Default bid response" - def bidPrice = 0.15 + def bidPrice = PBSUtils.getRandomFloorValue(0.11, 0.14) def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { seatbid[0].bid[0].price = bidPrice - seatbid[0].bid.add(Bid.getDefaultBids(bidRequest.imp)[0]) - seatbid[0].bid[1].price = bidPrice } bidder.setResponse(bidRequest.id, bidResponse) @@ -135,10 +132,9 @@ class BidRoundingSpec extends BaseSpec { def response = defaultPbsService.sendAuctionRequest(bidRequest) then: "Targeting hb_pb should be round" - def targeting = response.seatbid[0].bid.ext.prebid.targeting - assert targeting.collectEntries().findAll { it -> it.key.toString().contains("hb_pb_") } - .values().sort() == [getRoundedTargetingValueWithDefaultPrecision(bidPrice), - getRoundedTargetingValueWithUpPrecision(bidPrice)].sort() + def targeting = response.seatbid[0].bid[0].ext.prebid.targeting + assert targeting["hb_pb"] == getRoundedTargetingValueWithDefaultPrecision(bidPrice) || + getRoundedTargetingValueWithUpPrecision(bidPrice) where: accountAuctionConfig << [new AccountAuctionConfig(bidRounding: TIME_SPLIT), From d5059e73aa73ad3cdb5c1846ba127355624ded40 Mon Sep 17 00:00:00 2001 From: markiian Date: Thu, 15 May 2025 16:22:08 +0300 Subject: [PATCH 6/7] Remove multi bid request --- .../prebid/server/functional/tests/BidRoundingSpec.groovy | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy index 1fb6739ce40..513db643b22 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy @@ -4,13 +4,11 @@ import org.prebid.server.functional.model.config.AccountAuctionConfig import org.prebid.server.functional.model.config.AccountConfig import org.prebid.server.functional.model.db.Account import org.prebid.server.functional.model.request.auction.BidRequest -import org.prebid.server.functional.model.request.auction.MultiBid import org.prebid.server.functional.model.request.auction.Targeting import org.prebid.server.functional.model.response.auction.BidResponse import org.prebid.server.functional.util.PBSUtils import static org.prebid.server.functional.model.AccountStatus.ACTIVE -import static org.prebid.server.functional.model.bidder.BidderName.GENERIC import static org.prebid.server.functional.model.request.auction.BidRounding.DOWN import static org.prebid.server.functional.model.request.auction.BidRounding.TIME_SPLIT import static org.prebid.server.functional.model.request.auction.BidRounding.TRUE @@ -113,8 +111,7 @@ class BidRoundingSpec extends BaseSpec { def "PBS should round bid value to the 50% down and 50% up when account bid rounding time split"() { given: "Default bid request" def bidRequest = BidRequest.getDefaultBidRequest().tap { - ext.prebid.targeting = new Targeting(includeBidderKeys: true) - ext.prebid.multibid = [new MultiBid(bidder: GENERIC, maxBids: 2, targetBidderCodePrefix: GENERIC.value)] + ext.prebid.targeting = new Targeting() } and: "Account in the DB" @@ -122,7 +119,7 @@ class BidRoundingSpec extends BaseSpec { accountDao.save(account) and: "Default bid response" - def bidPrice = PBSUtils.getRandomFloorValue(0.11, 0.14) + def bidPrice = PBSUtils.randomFloorValue def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { seatbid[0].bid[0].price = bidPrice } From 2c42c1fa8ced579b1ee1c076d94f88af09d299ff Mon Sep 17 00:00:00 2001 From: markiian Date: Fri, 23 May 2025 18:23:10 +0300 Subject: [PATCH 7/7] Update warning --- .../server/functional/tests/AmpSpec.groovy | 2 +- .../server/functional/tests/BaseSpec.groovy | 17 ++++- .../functional/tests/BidRoundingSpec.groovy | 72 +++++-------------- .../PriceFloorsEnforcementSpec.groovy | 2 +- 4 files changed, 35 insertions(+), 58 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/AmpSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/AmpSpec.groovy index 78d6b03016d..fa72326fe7c 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/AmpSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/AmpSpec.groovy @@ -86,7 +86,7 @@ class AmpSpec extends BaseSpec { then: "Response should contain information from stored response" def price = storedAuctionResponse.bid[0].price - assert response.targeting["hb_pb"] == getRoundedTargetingValueWithDefaultPrecision(price) + assert response.targeting["hb_pb"] == getRoundedTargetingValueWithDownPrecision(price) assert response.targeting["hb_size"] == "${storedAuctionResponse.bid[0].weight}x${storedAuctionResponse.bid[0].height}" and: "PBS not send request to bidder" diff --git a/src/test/groovy/org/prebid/server/functional/tests/BaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/BaseSpec.groovy index 3b8f354b237..123dbb25505 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/BaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/BaseSpec.groovy @@ -19,7 +19,10 @@ import org.prebid.server.functional.util.ObjectMapperWrapper import org.prebid.server.functional.util.PBSUtils import spock.lang.Specification +import java.math.RoundingMode + import static java.math.RoundingMode.DOWN +import static java.math.RoundingMode.HALF_UP import static java.math.RoundingMode.UP import static org.prebid.server.functional.testcontainers.Dependencies.networkServiceContainer import static org.prebid.server.functional.util.SystemProperties.DEFAULT_TIMEOUT @@ -81,12 +84,16 @@ abstract class BaseSpec extends Specification implements ObjectMapperWrapper { logs.findAll { it.contains(text) } } - protected static String getRoundedTargetingValueWithDefaultPrecision(BigDecimal value) { - "${value.setScale(DEFAULT_TARGETING_PRECISION, DOWN)}0" + protected static String getRoundedTargetingValueWithDownPrecision(BigDecimal value) { + roundWithDefaultPrecisionAndRoundingType(value, DOWN) + } + + protected static String getRoundedTargetingValueWithHalfUpPrecision(BigDecimal value) { + roundWithDefaultPrecisionAndRoundingType(value, HALF_UP) } protected static String getRoundedTargetingValueWithUpPrecision(BigDecimal value) { - "${value.setScale(DEFAULT_TARGETING_PRECISION, UP )}0" + roundWithDefaultPrecisionAndRoundingType(value, UP) } protected static Map> getRequests(BidResponse bidResponse) { @@ -105,4 +112,8 @@ abstract class BaseSpec extends Specification implements ObjectMapperWrapper { List bidderCalls) { [(bidderName): bidderCalls.collect { bidderCall -> decode(bidderCall.requestBody as String, BidderRequest) }] } + + private static GString roundWithDefaultPrecisionAndRoundingType(BigDecimal value, RoundingMode roundingMode) { + "${value.setScale(DEFAULT_TARGETING_PRECISION, roundingMode)}0" + } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy index 513db643b22..15095aaa3e8 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/BidRoundingSpec.groovy @@ -4,23 +4,21 @@ import org.prebid.server.functional.model.config.AccountAuctionConfig import org.prebid.server.functional.model.config.AccountConfig import org.prebid.server.functional.model.db.Account import org.prebid.server.functional.model.request.auction.BidRequest -import org.prebid.server.functional.model.request.auction.Targeting import org.prebid.server.functional.model.response.auction.BidResponse import org.prebid.server.functional.util.PBSUtils import static org.prebid.server.functional.model.AccountStatus.ACTIVE import static org.prebid.server.functional.model.request.auction.BidRounding.DOWN -import static org.prebid.server.functional.model.request.auction.BidRounding.TIME_SPLIT import static org.prebid.server.functional.model.request.auction.BidRounding.TRUE import static org.prebid.server.functional.model.request.auction.BidRounding.UNKNOWN import static org.prebid.server.functional.model.request.auction.BidRounding.UP class BidRoundingSpec extends BaseSpec { - def "PBS should round bid value to the down when account bid rounding empty or unknown"() { + def "PBS should round bid value to the down when account bid rounding setting is #bidRoundingValue"() { given: "Default bid request" def bidRequest = BidRequest.getDefaultBidRequest().tap { - ext.prebid.targeting = new Targeting() + enableCache() } and: "Account in the DB" @@ -39,25 +37,27 @@ class BidRoundingSpec extends BaseSpec { then: "Targeting hb_pb should be round" def targeting = response.seatbid[0].bid[0].ext.prebid.targeting - assert targeting["hb_pb"] == getRoundedTargetingValueWithDefaultPrecision(bidPrice) + assert targeting["hb_pb"] == getRoundedTargetingValueWithDownPrecision(bidPrice) where: bidRoundingValue << [new AccountAuctionConfig(bidRounding: null), - new AccountAuctionConfig(bidRounding: UNKNOWN)] + new AccountAuctionConfig(bidRounding: UNKNOWN), + new AccountAuctionConfig(bidRounding: DOWN), + new AccountAuctionConfig(bidRoundingSnakeCase: DOWN)] } - def "PBS should round bid value to the up when account bid rounding UP or TRUE"() { + def "PBS should round bid value to the up when account bid rounding setting is #bidRoundingValue"() { given: "Default bid request" def bidRequest = BidRequest.getDefaultBidRequest().tap { - ext.prebid.targeting = new Targeting() + enableCache() } and: "Account in the DB" - def account = getAccountWithBidRounding(bidRequest.accountId, accountAuctionConfig) + def account = getAccountWithBidRounding(bidRequest.accountId, bidRoundingValue) accountDao.save(account) and: "Default bid response" - def bidPrice = PBSUtils.getRandomFloorValue(0.15, 0.19) + def bidPrice = PBSUtils.getRandomFloorValue() def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { seatbid[0].bid[0].price = bidPrice } @@ -71,55 +71,22 @@ class BidRoundingSpec extends BaseSpec { assert targeting["hb_pb"] == getRoundedTargetingValueWithUpPrecision(bidPrice) where: - accountAuctionConfig << [new AccountAuctionConfig(bidRounding: UP), - new AccountAuctionConfig(bidRounding: TRUE), - new AccountAuctionConfig(bidRoundingSnakeCase: UP), - new AccountAuctionConfig(bidRoundingSnakeCase: TRUE)] + bidRoundingValue << [new AccountAuctionConfig(bidRounding: UP), + new AccountAuctionConfig(bidRoundingSnakeCase: UP)] } - def "PBS should round bid value to the down when account bid rounding DOWN or TRUE"() { + def "PBS should round bid value to the up or down when account bid rounding setting is #bidRoundingValue"() { given: "Default bid request" def bidRequest = BidRequest.getDefaultBidRequest().tap { - ext.prebid.targeting = new Targeting() + enableCache() } and: "Account in the DB" - def account = getAccountWithBidRounding(bidRequest.accountId, accountAuctionConfig) - accountDao.save(account) - - and: "Default bid response" - def bidPrice = PBSUtils.getRandomFloorValue(0.11, 0.14) - def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { - seatbid[0].bid[0].price = bidPrice - } - bidder.setResponse(bidRequest.id, bidResponse) - - when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) - - then: "Targeting hb_pb should be round" - def targeting = response.seatbid[0].bid[0].ext.prebid.targeting - assert targeting["hb_pb"] == getRoundedTargetingValueWithDefaultPrecision(bidPrice) - - where: - accountAuctionConfig << [new AccountAuctionConfig(bidRounding: DOWN), - new AccountAuctionConfig(bidRounding: TRUE), - new AccountAuctionConfig(bidRoundingSnakeCase: DOWN), - new AccountAuctionConfig(bidRoundingSnakeCase: TRUE)] - } - - def "PBS should round bid value to the 50% down and 50% up when account bid rounding time split"() { - given: "Default bid request" - def bidRequest = BidRequest.getDefaultBidRequest().tap { - ext.prebid.targeting = new Targeting() - } - - and: "Account in the DB" - def account = getAccountWithBidRounding(bidRequest.accountId, accountAuctionConfig) + def account = getAccountWithBidRounding(bidRequest.accountId, bidRoundingValue) accountDao.save(account) and: "Default bid response" - def bidPrice = PBSUtils.randomFloorValue + def bidPrice = PBSUtils.getRandomFloorValue() def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { seatbid[0].bid[0].price = bidPrice } @@ -130,12 +97,11 @@ class BidRoundingSpec extends BaseSpec { then: "Targeting hb_pb should be round" def targeting = response.seatbid[0].bid[0].ext.prebid.targeting - assert targeting["hb_pb"] == getRoundedTargetingValueWithDefaultPrecision(bidPrice) || - getRoundedTargetingValueWithUpPrecision(bidPrice) + assert targeting["hb_pb"] == getRoundedTargetingValueWithHalfUpPrecision(bidPrice) where: - accountAuctionConfig << [new AccountAuctionConfig(bidRounding: TIME_SPLIT), - new AccountAuctionConfig(bidRoundingSnakeCase: TIME_SPLIT)] + bidRoundingValue << [new AccountAuctionConfig(bidRounding: TRUE), + new AccountAuctionConfig(bidRoundingSnakeCase: TRUE)] } private static final Account getAccountWithBidRounding(String accountId, AccountAuctionConfig accountAuctionConfig) { diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsEnforcementSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsEnforcementSpec.groovy index 4d47947670a..cec8bb8fa41 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsEnforcementSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsEnforcementSpec.groovy @@ -77,7 +77,7 @@ class PriceFloorsEnforcementSpec extends PriceFloorsBaseSpec { def response = floorsPbsService.sendAmpRequest(ampRequest) then: "PBS should suppress bids lower than floorRuleValue" - def bidPrice = getRoundedTargetingValueWithDefaultPrecision(floorValue) + def bidPrice = getRoundedTargetingValueWithDownPrecision(floorValue) verifyAll(response) { targeting["hb_pb_generic"] == bidPrice targeting["hb_pb"] == bidPrice