diff --git a/src/main/java/org/prebid/server/auction/BidResponseCreator.java b/src/main/java/org/prebid/server/auction/BidResponseCreator.java index f983830e37f..f1c3d1f0d82 100644 --- a/src/main/java/org/prebid/server/auction/BidResponseCreator.java +++ b/src/main/java/org/prebid/server/auction/BidResponseCreator.java @@ -1566,7 +1566,15 @@ private Bid toBid(BidInfo bidInfo, final String categoryDuration = bidInfo.getCategory(); targetingKeywords = keywordsCreator != null ? keywordsCreator.makeFor( - bid, seat, isWinningBid, cacheId, bidType.getName(), videoCacheId, categoryDuration, account) + bid, + seat, + isWinningBid, + cacheId, + bidType.getName(), + videoCacheId, + categoryDuration, + account, + bidWarnings) : null; } else { targetingKeywords = null; diff --git a/src/main/java/org/prebid/server/auction/CpmRange.java b/src/main/java/org/prebid/server/auction/CpmRange.java index 00133373474..0310f956025 100644 --- a/src/main/java/org/prebid/server/auction/CpmRange.java +++ b/src/main/java/org/prebid/server/auction/CpmRange.java @@ -1,7 +1,6 @@ package org.prebid.server.auction; import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.StringUtils; import org.prebid.server.proto.openrtb.ext.request.ExtGranularityRange; import org.prebid.server.settings.model.Account; import org.prebid.server.settings.model.AccountAuctionConfig; @@ -19,6 +18,8 @@ */ public class CpmRange { + public static final String DEFAULT_CPM = "0.0"; + private static final Locale LOCALE = Locale.US; private static final int DEFAULT_PRECISION = 2; @@ -30,7 +31,7 @@ private CpmRange() { */ public static String fromCpm(BigDecimal cpm, PriceGranularity priceGranularity, Account account) { final BigDecimal value = fromCpmAsNumber(cpm, priceGranularity, account); - return value != null ? format(value, priceGranularity.getPrecision()) : StringUtils.EMPTY; + return value != null ? format(value, priceGranularity.getPrecision()) : DEFAULT_CPM; } /** diff --git a/src/main/java/org/prebid/server/auction/ExchangeService.java b/src/main/java/org/prebid/server/auction/ExchangeService.java index 3d48565906c..8bea0bc315e 100644 --- a/src/main/java/org/prebid/server/auction/ExchangeService.java +++ b/src/main/java/org/prebid/server/auction/ExchangeService.java @@ -782,7 +782,6 @@ private AuctionParticipation createAuctionParticipation( bidderToMultiBid, biddersToConfigs, bidderToPrebidBidders, - bidderAliases, context); final Map originalPriceFloors = enrichedWithPriceFloors.getImp().stream() @@ -815,7 +814,6 @@ private BidRequest prepareBidRequest(BidderPrivacyResult bidderPrivacyResult, Map bidderToMultiBid, Map biddersToConfigs, Map bidderToPrebidBidders, - BidderAliases bidderAliases, AuctionContext context) { final String bidder = bidderPrivacyResult.getRequestBidder(); @@ -869,7 +867,6 @@ private BidRequest prepareBidRequest(BidderPrivacyResult bidderPrivacyResult, transmitTid, useFirstPartyData, context.getAccount(), - bidderAliases, context.getDebugWarnings()); return bidRequest.toBuilder() @@ -907,13 +904,12 @@ private List prepareImps(String bidder, boolean transmitTid, boolean useFirstPartyData, Account account, - BidderAliases bidderAliases, List debugWarnings) { return bidRequest.getImp().stream() .filter(imp -> bidderParamsFromImpExt(imp.getExt()).hasNonNull(bidder)) .map(imp -> imp.toBuilder().ext(imp.getExt().deepCopy()).build()) - .map(imp -> impAdjuster.adjust(imp, bidder, bidderAliases, debugWarnings)) + .map(imp -> impAdjuster.adjust(imp, bidder, debugWarnings)) .map(imp -> prepareImp(imp, bidder, bidRequest, transmitTid, useFirstPartyData, account, debugWarnings)) .toList(); } diff --git a/src/main/java/org/prebid/server/auction/ImpAdjuster.java b/src/main/java/org/prebid/server/auction/ImpAdjuster.java index 2d8be424fea..738622e6a86 100644 --- a/src/main/java/org/prebid/server/auction/ImpAdjuster.java +++ b/src/main/java/org/prebid/server/auction/ImpAdjuster.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.node.IntNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.Imp; -import org.prebid.server.auction.aliases.BidderAliases; +import org.apache.commons.lang3.StringUtils; import org.prebid.server.json.JacksonMapper; import org.prebid.server.json.JsonMerger; import org.prebid.server.validation.ImpValidator; @@ -36,7 +36,7 @@ public ImpAdjuster(JacksonMapper jacksonMapper, this.jsonMerger = Objects.requireNonNull(jsonMerger); } - public Imp adjust(Imp originalImp, String bidder, BidderAliases bidderAliases, List debugMessages) { + public Imp adjust(Imp originalImp, String bidder, List debugMessages) { setAeParams(originalImp.getExt()); final JsonNode impExtPrebidImp = bidderParamsFromImpExtPrebidImp(originalImp.getExt()); @@ -44,7 +44,7 @@ public Imp adjust(Imp originalImp, String bidder, BidderAliases bidderAliases, L return originalImp; } - final JsonNode bidderNode = getBidderNode(bidder, bidderAliases, impExtPrebidImp); + final JsonNode bidderNode = getBidderNode(bidder, impExtPrebidImp); if (bidderNode == null || bidderNode.isEmpty()) { removeImpExtPrebidImp(originalImp.getExt()); @@ -98,11 +98,11 @@ private static JsonNode bidderParamsFromImpExtPrebidImp(ObjectNode ext) { .orElse(null); } - private static JsonNode getBidderNode(String bidderName, BidderAliases bidderAliases, JsonNode node) { + private static JsonNode getBidderNode(String bidderName, JsonNode node) { final Iterator fieldNames = node.fieldNames(); while (fieldNames.hasNext()) { final String fieldName = fieldNames.next(); - if (bidderAliases.isSame(fieldName, bidderName)) { + if (StringUtils.equalsIgnoreCase(fieldName, bidderName)) { return node.get(fieldName); } } diff --git a/src/main/java/org/prebid/server/auction/TargetingKeywordsCreator.java b/src/main/java/org/prebid/server/auction/TargetingKeywordsCreator.java index b873210c1f3..16af4e15d71 100644 --- a/src/main/java/org/prebid/server/auction/TargetingKeywordsCreator.java +++ b/src/main/java/org/prebid/server/auction/TargetingKeywordsCreator.java @@ -2,7 +2,9 @@ import com.iab.openrtb.response.Bid; import org.apache.commons.lang3.StringUtils; +import org.prebid.server.bidder.model.BidderError; import org.prebid.server.proto.openrtb.ext.request.ExtPriceGranularity; +import org.prebid.server.proto.openrtb.ext.response.ExtBidderError; import org.prebid.server.settings.model.Account; import java.math.BigDecimal; @@ -12,7 +14,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; /** * Used throughout Prebid to create targeting keys as keys which can be used in an ad server like DFP. @@ -77,8 +78,6 @@ public class TargetingKeywordsCreator { */ private static final String FORMAT_KEY = "_format"; - private static final String DEFAULT_CPM = "0.0"; - private final PriceGranularity priceGranularity; private final boolean includeWinners; private final boolean includeBidderKeys; @@ -154,7 +153,8 @@ Map makeFor(Bid bid, String format, String vastCacheId, String categoryDuration, - Account account) { + Account account, + Map> bidWarnings) { final Map keywords = makeFor( bidder, @@ -170,13 +170,13 @@ Map makeFor(Bid bid, account); if (resolver == null) { - return truncateKeys(keywords); + return truncateKeys(keywords, bidWarnings); } final Map augmentedKeywords = new HashMap<>(keywords); augmentedKeywords.putAll(resolver.resolve(bid, bidder)); - return truncateKeys(augmentedKeywords); + return truncateKeys(augmentedKeywords, bidWarnings); } /** @@ -204,7 +204,7 @@ private Map makeFor(String bidder, final String roundedCpm = isPriceGranularityValid() ? CpmRange.fromCpm(price, priceGranularity, account) - : DEFAULT_CPM; + : CpmRange.DEFAULT_CPM; keywordMap.put(this.keyPrefix + PB_KEY, roundedCpm); @@ -261,12 +261,33 @@ private static String sizeFrom(Integer width, Integer height) { : null; } - private Map truncateKeys(Map keyValues) { - return truncateAttrChars > 0 - ? keyValues.entrySet().stream() - .collect(Collectors - .toMap(keyValue -> truncateKey(keyValue.getKey()), Map.Entry::getValue, (key1, key2) -> key1)) - : keyValues; + private Map truncateKeys(Map keyValues, + Map> bidWarnings) { + + if (truncateAttrChars <= 0) { + return keyValues; + } + + final Map keys = new HashMap<>(); + final List truncatedKeys = new ArrayList<>(); + for (Map.Entry entry : keyValues.entrySet()) { + final String key = entry.getKey(); + final String truncatedKey = truncateKey(key); + keys.putIfAbsent(truncatedKey, entry.getValue()); + + if (truncatedKey.length() != key.length()) { + truncatedKeys.add(key); + } + } + + if (!truncatedKeys.isEmpty()) { + final String errorMessage = "The following keys have been truncated: %s" + .formatted(String.join(", ", truncatedKeys)); + bidWarnings.computeIfAbsent("targeting", ignored -> new ArrayList<>()) + .add(ExtBidderError.of(BidderError.Type.bad_input.getCode(), errorMessage)); + } + + return keys; } private String truncateKey(String key) { diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/PbsConfig.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/PbsConfig.groovy index 052bcf2f69f..a63039f3416 100644 --- a/src/test/groovy/org/prebid/server/functional/testcontainers/PbsConfig.groovy +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/PbsConfig.groovy @@ -145,5 +145,9 @@ LIMIT 1 "currency-converter.external-rates.refresh-period-ms" : "900000"] } + static Map getTargetingConfig() { + ["settings.targeting.truncate-attr-chars": '255'] + } + private PbsConfig() {} } diff --git a/src/test/groovy/org/prebid/server/functional/testcontainers/container/PrebidServerContainer.groovy b/src/test/groovy/org/prebid/server/functional/testcontainers/container/PrebidServerContainer.groovy index e13fcae3764..0daa6883acf 100644 --- a/src/test/groovy/org/prebid/server/functional/testcontainers/container/PrebidServerContainer.groovy +++ b/src/test/groovy/org/prebid/server/functional/testcontainers/container/PrebidServerContainer.groovy @@ -42,6 +42,7 @@ class PrebidServerContainer extends GenericContainer { << PbsConfig.bidderAliasConfig << PbsConfig.prebidCacheConfig << PbsConfig.mySqlConfig + << PbsConfig.targetingConfig withConfig(commonConfig) withConfig(customConfig) } diff --git a/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy index b745ae53a1f..dd3e010ca0f 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/CacheSpec.groovy @@ -27,7 +27,6 @@ class CacheSpec extends BaseSpec { private static final String PBS_API_HEADER = 'x-pbc-api-key' private static final Integer MAX_DATACENTER_REGION_LENGTH = 4 private static final Integer DEFAULT_UUID_LENGTH = 36 - private static final Integer TARGETING_PARAM_NAME_MAX_LENGTH = 20 private static final String XML_CREATIVE_SIZE_ACCOUNT_METRIC = "account.%s.prebid_cache.creative_size.xml" private static final String JSON_CREATIVE_SIZE_ACCOUNT_METRIC = "account.%s.prebid_cache.creative_size.json" @@ -612,8 +611,8 @@ class CacheSpec extends BaseSpec { it.get("hb_cache_id_generic") it.get("hb_cache_path") == CACHE_PATH it.get("hb_cache_host") == CACHE_HOST - it.get("hb_cache_path_generic".substring(0, TARGETING_PARAM_NAME_MAX_LENGTH)) == CACHE_PATH - it.get("hb_cache_host_generic".substring(0, TARGETING_PARAM_NAME_MAX_LENGTH)) == CACHE_HOST + it.get("hb_cache_path_generic") == CACHE_PATH + it.get("hb_cache_host_generic") == CACHE_HOST } and: "Debug should contain http call" @@ -648,8 +647,8 @@ class CacheSpec extends BaseSpec { it.get("hb_cache_id_generic") it.get("hb_cache_path") == INTERNAL_CACHE_PATH it.get("hb_cache_host") == networkServiceContainer.hostAndPort.toString() - it.get("hb_cache_path_generic".substring(0, TARGETING_PARAM_NAME_MAX_LENGTH)) == INTERNAL_CACHE_PATH - it.get("hb_cache_host_generic".substring(0, TARGETING_PARAM_NAME_MAX_LENGTH)) == networkServiceContainer.hostAndPort.toString() + it.get("hb_cache_path_generic") == INTERNAL_CACHE_PATH + it.get("hb_cache_host_generic") == networkServiceContainer.hostAndPort.toString() } and: "Debug should contain http call" diff --git a/src/test/groovy/org/prebid/server/functional/tests/ImpRequestSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/ImpRequestSpec.groovy index 4cc3b728449..7a2f5923d56 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/ImpRequestSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/ImpRequestSpec.groovy @@ -19,6 +19,7 @@ import static org.prebid.server.functional.model.bidder.BidderName.OPENX import static org.prebid.server.functional.model.bidder.BidderName.RUBICON import static org.prebid.server.functional.model.bidder.BidderName.UNKNOWN import static org.prebid.server.functional.model.bidder.BidderName.WILDCARD +import static org.prebid.server.functional.model.bidder.BidderName.GENER_X import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID import static org.prebid.server.functional.testcontainers.Dependencies.getNetworkServiceContainer @@ -131,6 +132,42 @@ class ImpRequestSpec extends BaseSpec { ALIAS_CAMEL_CASE | GENERIC_CAMEL_CASE } + def "PBS should update imp fields only for specific alias when request has multiple aliases"() { + given: "Default basic BidRequest" + def storedPmp = Pmp.defaultPmp + def originalPmp = Pmp.defaultPmp + def bidRequest = BidRequest.defaultBidRequest.tap { + imp.first.tap { + pmp = originalPmp + ext.prebid.imp = [(aliasName): new Imp(pmp: storedPmp)] + ext.prebid.bidder.generic = null + ext.prebid.bidder.generX = new Generic() + ext.prebid.bidder.alias = new Generic() + } + ext.prebid.aliases = [(GENER_X.value) : bidderName, + (aliasName.value): bidderName, + ] + } + + when: "Requesting PBS auction" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "BidderRequest should update imp information for specific alias" + def bidderRequests = getRequests(response) + assert bidderRequests.size() == 2 + assert bidderRequests[ALIAS.value].imp.pmp.flatten() == [storedPmp] + + and: "Left original information for other" + assert bidderRequests[GENER_X.value].imp.pmp.flatten() == [originalPmp] + + where: + aliasName | bidderName + ALIAS | GENERIC + ALIAS_CAMEL_CASE | GENERIC + ALIAS | GENERIC_CAMEL_CASE + ALIAS_CAMEL_CASE | GENERIC_CAMEL_CASE + } + def "PBS shouldn't update imp fields when imp.ext.prebid.imp contain only bidder with invalid name"() { given: "Default basic BidRequest" def impPmp = Pmp.defaultPmp diff --git a/src/test/groovy/org/prebid/server/functional/tests/TargetingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/TargetingSpec.groovy index 960e05b458e..d9d337b3c27 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/TargetingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/TargetingSpec.groovy @@ -34,6 +34,7 @@ import org.prebid.server.functional.model.response.auction.Prebid import org.prebid.server.functional.model.response.auction.SeatBid import org.prebid.server.functional.service.PrebidServerException import org.prebid.server.functional.service.PrebidServerService +import org.prebid.server.functional.testcontainers.PbsConfig import org.prebid.server.functional.testcontainers.scaffolding.Bidder import org.prebid.server.functional.util.PBSUtils @@ -57,11 +58,29 @@ class TargetingSpec extends BaseSpec { private static final Integer MAX_AMP_TARGETING_TRUNCATION_LENGTH = 11 private static final String DEFAULT_TARGETING_PREFIX = "hb" private static final Integer TARGETING_PREFIX_LENGTH = 11 - private static final Integer MAX_TRUNCATE_ATTR_CHARS = 255 private static final Integer MAX_BIDS_RANKING = 3 private static final String HB_ENV_AMP = "amp" private static final Integer MAIN_RANK = 1 private static final Integer SUBORDINATE_RANK = 2 + private static final String EMPTY_CPM = "0.0" + private static final Integer DEFAULT_TRUNCATE_CHARS = 20 + private static final Integer EXTENDED_TRUNCATE_CHARS = PbsConfig.targetingConfig.get('settings.targeting.truncate-attr-chars').toInteger() + private static final Map EMPTY_TARGETING_CONFIG = ['settings.targeting.truncate-attr-chars': null] as Map + private static final Map ONLY_WINNING_BIDS_CONFIG = ["auction.cache.only-winning-bids": "true"] + private static final Map DISABLED_ONLY_WINNING_BIDS_CONFIG = ["auction.cache.only-winning-bids": "false"] + private static final String DROP_PREFIX_WARNING = "Key prefix value is dropped to default. " + + "Decrease custom prefix length or increase truncateattrchars by %s" + private static final String TRUNCATED_WARNING = "The following keys have been truncated:" + + private static final PrebidServerService pbsWithDefaultTargetingLength = pbsServiceFactory.getService(EMPTY_TARGETING_CONFIG) + private static final PrebidServerService pbsWithOnlyWinningBids = pbsServiceFactory.getService(EMPTY_TARGETING_CONFIG + ONLY_WINNING_BIDS_CONFIG) + private static final PrebidServerService pbsWithDisabledOnlyWinningBids = pbsServiceFactory.getService(EMPTY_TARGETING_CONFIG + DISABLED_ONLY_WINNING_BIDS_CONFIG) + + def cleanupSpec() { + pbsServiceFactory.removeContainer(EMPTY_TARGETING_CONFIG + ONLY_WINNING_BIDS_CONFIG) + pbsServiceFactory.removeContainer(EMPTY_TARGETING_CONFIG + DISABLED_ONLY_WINNING_BIDS_CONFIG) + pbsServiceFactory.removeContainer(EMPTY_TARGETING_CONFIG) + } def "PBS should include targeting bidder specific keys when alwaysIncludeDeals is true and deal bid wins"() { given: "Bid request with alwaysIncludeDeals = true" @@ -83,7 +102,7 @@ class TargetingSpec extends BaseSpec { def bidderName = GENERIC.value when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS response targeting contains bidder specific keys" def targetingKeyMap = response.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting @@ -109,7 +128,7 @@ class TargetingSpec extends BaseSpec { bidder.setResponse(bidRequest.id, bidResponse) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS response targeting contains bidder specific keys" def targetingKeyMap = response.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting @@ -134,7 +153,7 @@ class TargetingSpec extends BaseSpec { bidder.setResponse(bidRequest.id, bidResponse) when: "PBS processes auction request" - def response = getEnabledWinBidsPbsService().sendAuctionRequest(bidRequest) + def response = pbsWithOnlyWinningBids.sendAuctionRequest(bidRequest) then: "PBS response targeting does not contain bidder specific keys" def targetingKeyMap = response.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting @@ -162,7 +181,7 @@ class TargetingSpec extends BaseSpec { def bidderName = GENERIC.value when: "PBS processes auction request" - def response = getDisabledWinBidsPbsService().sendAuctionRequest(bidRequest) + def response = pbsWithDisabledOnlyWinningBids.sendAuctionRequest(bidRequest) then: "PBS response targeting contains bidder specific keys" def targetingKeyMap = response.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting @@ -189,7 +208,7 @@ class TargetingSpec extends BaseSpec { } when: "Requesting PBS auction" - def bidResponse = defaultPbsService.sendAuctionRequest(bidRequest) + def bidResponse = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS response shouldn't contain targeting in response" assert !bidResponse.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting @@ -213,7 +232,7 @@ class TargetingSpec extends BaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp request" - def response = defaultPbsService.sendAmpRequest(ampRequest) + def response = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp response shouldn't contain targeting" assert !response.targeting @@ -235,7 +254,7 @@ class TargetingSpec extends BaseSpec { bidder.setResponse(bidRequest.id, bidResponse) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS response targeting includes only one deal specific key" def targetingKeyMap = response.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting @@ -264,7 +283,7 @@ class TargetingSpec extends BaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp request" - defaultPbsService.sendAmpRequest(ampRequest) + pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Bidder request should contain amp query params in ext.prebid.amp.data" def bidderRequest = bidder.getBidderRequest(ampStoredRequest.id) @@ -326,7 +345,7 @@ class TargetingSpec extends BaseSpec { storedResponseDao.save(storedResponse) when: "PBS processes amp request" - def response = defaultPbsService.sendAmpRequest(ampRequest) + def response = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp response targeting should contain ad server targeting key" verifyAll { @@ -358,7 +377,7 @@ class TargetingSpec extends BaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp request" - def response = defaultPbsService.sendAmpRequest(ampRequest) + def response = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp response shouldn't contain custom targeting" assert !response.targeting[customKey] @@ -392,7 +411,7 @@ class TargetingSpec extends BaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp request" - def response = defaultPbsService.sendAmpRequest(ampRequest) + def response = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp response shouldn't contain custom targeting with full naming" assert !response.targeting[customKey] @@ -406,7 +425,7 @@ class TargetingSpec extends BaseSpec { def pbsConfig = [ "adapters.openx.enabled" : "true", "adapters.openx.endpoint": "$networkServiceContainer.rootUri/auction".toString()] - def defaultPbsService = pbsServiceFactory.getService(pbsConfig) + def pbsWithDefaultTargetingLength = pbsServiceFactory.getService(pbsConfig) and: "Default bid request" def accountId = PBSUtils.randomNumber as String @@ -423,13 +442,16 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "Response should contain targeting with corresponding length" assert response.seatbid.bid.ext.prebid.targeting .every(list -> list .every(map -> map.keySet() .every(key -> key.length() <= targetingLength))) + + cleanup: "Stop and remove pbs container" + pbsServiceFactory.removeContainer(pbsConfig) } def "PBS should truncate targeting corresponding to value in account config when in account define truncate target attr"() { @@ -445,7 +467,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes amp request" - def response = defaultPbsService.sendAmpRequest(ampRequest) + def response = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Response should contain in targeting key not biggest that max size define in account" assert response.targeting.keySet().every { str -> str.length() <= MAX_AMP_TARGETING_TRUNCATION_LENGTH } @@ -467,7 +489,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes amp request" - def response = defaultPbsService.sendAmpRequest(ampRequest) + def response = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Response shouldn't contain targeting" assert response.targeting.isEmpty() @@ -497,7 +519,7 @@ class TargetingSpec extends BaseSpec { and: "Create and save stored response into DB" def storedBidResponse = BidResponse.getDefaultBidResponse(ampStoredRequest).tap { - seatbid[0].bid[0].price = max.plus(1) + seatbid[0].bid[0].price = max + 1 } def storedResponse = new StoredResponse(responseId: storedBidResponseId, storedBidResponse: storedBidResponse) storedResponseDao.save(storedResponse) @@ -507,7 +529,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes amp request" - def response = defaultPbsService.sendAmpRequest(ampRequest) + def response = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Response should contain targeting hb_pb" assert response.targeting["hb_pb"] == String.format("%,.2f", max.setScale(precision, RoundingMode.DOWN)) @@ -530,19 +552,43 @@ class TargetingSpec extends BaseSpec { and: "Create and save stored response into DB" def storedBidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { - seatbid[0].bid[0].price = max.plus(1) + seatbid[0].bid[0].price = max + 1 } def storedResponse = new StoredResponse(responseId: storedBidResponseId, storedBidResponse: storedBidResponse) storedResponseDao.save(storedResponse) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "Response should contain targeting hb_pb" def targetingKeyMap = response.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting assert targetingKeyMap["hb_pb"] == String.format("%,.2f", max.setScale(precision, RoundingMode.DOWN)) } + def "PBS auction shouldn't delete bid and update targeting if price equal zero and dealId present"() { + given: "Default bid request with stored response" + def bidRequest = BidRequest.defaultBidRequest.tap { + ext.prebid.targeting = Targeting.createWithAllValuesSetTo(true) + } + + and: "Bid response with zero price" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { + seatbid[0].bid[0].price = 0 + seatbid[0].bid[0].dealid = PBSUtils.randomString + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Response should contain proper targeting hb_pb" + def targetingKeyMap = response.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting + assert targetingKeyMap["hb_pb"] == EMPTY_CPM + assert targetingKeyMap["hb_pb_generic"] == EMPTY_CPM + } + def "PBS auction should use default targeting prefix when ext.prebid.targeting.prefix is biggest that twenty"() { given: "Bid request with long targeting prefix" def prefix = PBSUtils.getRandomString(30) @@ -551,7 +597,7 @@ class TargetingSpec extends BaseSpec { } when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS response should contain default targeting prefix" def targeting = response.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting @@ -572,7 +618,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS response should contain default targeting prefix" def targeting = response.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting @@ -587,7 +633,7 @@ class TargetingSpec extends BaseSpec { } when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS response should contain default targeting prefix" def targeting = response.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting @@ -610,7 +656,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS response should contain default targeting prefix" def targeting = response.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting @@ -629,7 +675,7 @@ class TargetingSpec extends BaseSpec { } when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS response should contain targeting with requested prefix" def targeting = response.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting @@ -650,7 +696,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS response should contain targeting key with specified prefix in account level" def targeting = response.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting @@ -670,7 +716,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS response should contain targeting key with specified prefix in account level" def targeting = response.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting @@ -692,7 +738,7 @@ class TargetingSpec extends BaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp request" - def ampResponse = defaultPbsService.sendAmpRequest(ampRequest) + def ampResponse = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp response should contain default targeting prefix" def targeting = ampResponse.targeting @@ -718,7 +764,7 @@ class TargetingSpec extends BaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp request" - def ampResponse = defaultPbsService.sendAmpRequest(ampRequest) + def ampResponse = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp response should contain targeting response with custom prefix" def targeting = ampResponse.targeting @@ -743,7 +789,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes amp request" - def ampResponse = defaultPbsService.sendAmpRequest(ampRequest) + def ampResponse = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp response should contain targeting response with custom prefix" def targeting = ampResponse.targeting @@ -768,7 +814,7 @@ class TargetingSpec extends BaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp request" - def ampResponse = defaultPbsService.sendAmpRequest(ampRequest) + def ampResponse = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp response should contain targeting response with custom prefix" def targeting = ampResponse.targeting @@ -797,7 +843,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes amp request" - def ampResponse = defaultPbsService.sendAmpRequest(ampRequest) + def ampResponse = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp response should contain targeting response with custom prefix" def targeting = ampResponse.targeting @@ -820,7 +866,7 @@ class TargetingSpec extends BaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp request" - def ampResponse = defaultPbsService.sendAmpRequest(ampRequest) + def ampResponse = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp response should contain custom targeting prefix" def targeting = ampResponse.targeting @@ -848,7 +894,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes amp request" - def ampResponse = defaultPbsService.sendAmpRequest(ampRequest) + def ampResponse = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp response should contain targeting response with custom prefix" def targeting = ampResponse.targeting @@ -878,7 +924,7 @@ class TargetingSpec extends BaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp request" - defaultPbsService.sendAmpRequest(ampRequest) + pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp response should contain value from targeting in imp.ext.data" def bidderRequest = bidder.getBidderRequest(ampStoredRequest.id) @@ -886,12 +932,7 @@ class TargetingSpec extends BaseSpec { } def "PBS amp should use long account targeting prefix when settings.targeting.truncate-attr-chars override"() { - given: "PBS config with setting.targeting" - def prefixMaxChars = PBSUtils.getRandomNumber(35, MAX_TRUNCATE_ATTR_CHARS) - def prebidServerService = pbsServiceFactory.getService( - ["settings.targeting.truncate-attr-chars": prefixMaxChars as String]) - - and: "Default AmpRequest" + given: "Default AmpRequest" def ampRequest = AmpRequest.defaultAmpRequest and: "Bid request" @@ -902,13 +943,13 @@ class TargetingSpec extends BaseSpec { storedRequestDao.save(storedRequest) and: "Account in the DB" - def prefix = PBSUtils.getRandomString(prefixMaxChars - TARGETING_PREFIX_LENGTH) + def prefix = PBSUtils.getRandomString(DEFAULT_TRUNCATE_CHARS - TARGETING_PREFIX_LENGTH) def config = new AccountAuctionConfig(targeting: new Targeting(prefix: prefix)) def account = new Account(uuid: ampRequest.account, config: new AccountConfig(auction: config)) accountDao.save(account) when: "PBS processes amp request" - def ampResponse = prebidServerService.sendAmpRequest(ampRequest) + def ampResponse = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp response should contain targeting response with custom prefix" def targeting = ampResponse.targeting @@ -917,16 +958,11 @@ class TargetingSpec extends BaseSpec { } def "PBS amp should use long request targeting prefix when settings.targeting.truncate-attr-chars override"() { - given: "PBS config with setting.targeting" - def prefixMaxChars = PBSUtils.getRandomNumber(35, MAX_TRUNCATE_ATTR_CHARS) - def prebidServerService = pbsServiceFactory.getService( - ["settings.targeting.truncate-attr-chars": prefixMaxChars as String]) - - and: "Default AmpRequest" + given: "Default AmpRequest" def ampRequest = AmpRequest.defaultAmpRequest and: "Bid request with prefix" - def prefix = PBSUtils.getRandomString(prefixMaxChars - TARGETING_PREFIX_LENGTH) + def prefix = PBSUtils.getRandomString(EXTENDED_TRUNCATE_CHARS - TARGETING_PREFIX_LENGTH) def ampStoredRequest = BidRequest.defaultBidRequest.tap { ext.prebid.targeting = new Targeting(prefix: prefix) } @@ -936,7 +972,7 @@ class TargetingSpec extends BaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp request" - def ampResponse = prebidServerService.sendAmpRequest(ampRequest) + def ampResponse = defaultPbsService.sendAmpRequest(ampRequest) then: "Amp response should contain targeting response with custom prefix" def targeting = ampResponse.targeting @@ -945,19 +981,14 @@ class TargetingSpec extends BaseSpec { } def "PBS auction should use long request targeting prefix when settings.targeting.truncate-attr-chars override"() { - given: "PBS config with setting.targeting" - def prefixMaxChars = PBSUtils.getRandomNumber(35, MAX_TRUNCATE_ATTR_CHARS) - def prebidServerService = pbsServiceFactory.getService( - ["settings.targeting.truncate-attr-chars": prefixMaxChars as String]) - - and: "Bid request with prefix" - def prefix = PBSUtils.getRandomString(prefixMaxChars - TARGETING_PREFIX_LENGTH) + given: "Bid request with prefix" + def prefix = PBSUtils.getRandomString(EXTENDED_TRUNCATE_CHARS - TARGETING_PREFIX_LENGTH) def bidRequest = BidRequest.defaultBidRequest.tap { ext.prebid.targeting = new Targeting(prefix: prefix) } when: "PBS processes auction request" - def bidResponse = prebidServerService.sendAuctionRequest(bidRequest) + def bidResponse = defaultPbsService.sendAuctionRequest(bidRequest) then: "PBS response should contain default targeting prefix" def targeting = bidResponse.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting @@ -966,24 +997,19 @@ class TargetingSpec extends BaseSpec { } def "PBS auction should use long account targeting prefix when settings.targeting.truncate-attr-chars override"() { - given: "PBS config with setting.targeting" - def prefixMaxChars = PBSUtils.getRandomNumber(35, MAX_TRUNCATE_ATTR_CHARS) - def prebidServerService = pbsServiceFactory.getService( - ["settings.targeting.truncate-attr-chars": prefixMaxChars as String]) - - and: "Bid request with empty targeting" + given: "Bid request with empty targeting" def bidRequest = BidRequest.defaultBidRequest.tap { ext.prebid.targeting = new Targeting() } and: "Account in the DB" - def prefix = PBSUtils.getRandomString(prefixMaxChars - TARGETING_PREFIX_LENGTH) + def prefix = PBSUtils.getRandomString(EXTENDED_TRUNCATE_CHARS - TARGETING_PREFIX_LENGTH) def config = new AccountAuctionConfig(targeting: new Targeting(prefix: prefix)) def account = new Account(uuid: bidRequest.accountId, config: new AccountConfig(auction: config)) accountDao.save(account) when: "PBS processes auction request" - def bidResponse = prebidServerService.sendAuctionRequest(bidRequest) + def bidResponse = defaultPbsService.sendAuctionRequest(bidRequest) then: "PBS response should contain default targeting prefix" def targeting = bidResponse.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting @@ -992,12 +1018,7 @@ class TargetingSpec extends BaseSpec { } def "PBS amp should ignore and add a warning to ext.warnings when value of the account prefix is longer then settings.targeting.truncate-attr-chars"() { - given: "PBS config with setting.targeting" - def targetingChars = PBSUtils.getRandomNumber(2, 10) - def prebidServerService = pbsServiceFactory.getService( - ["settings.targeting.truncate-attr-chars": targetingChars as String]) - - and: "Default AmpRequest" + given: "Default AmpRequest" def ampRequest = AmpRequest.defaultAmpRequest and: "Bid request" @@ -1008,33 +1029,28 @@ class TargetingSpec extends BaseSpec { storedRequestDao.save(storedRequest) and: "Account in the DB" - def prefix = PBSUtils.getRandomString(targetingChars + 1) + def prefix = PBSUtils.getRandomString(DEFAULT_TRUNCATE_CHARS + 1) def config = new AccountAuctionConfig(targeting: new Targeting(prefix: prefix)) def account = new Account(uuid: ampRequest.account, config: new AccountConfig(auction: config)) accountDao.save(account) when: "PBS processes amp request" - def ampResponse = prebidServerService.sendAmpRequest(ampRequest) + def ampResponse = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp response should contain warning" - assert ampResponse.ext?.warnings[TARGETING]*.message == ["Key prefix value is dropped to default. " + - "Decrease custom prefix length or increase truncateattrchars by " + - "${prefix.length() + TARGETING_PREFIX_LENGTH - targetingChars}"] + def decreasePrefixLength = prefix.length() + TARGETING_PREFIX_LENGTH - DEFAULT_TRUNCATE_CHARS + assert ampResponse.ext?.warnings[TARGETING]*.message == [DROP_PREFIX_WARNING.formatted(decreasePrefixLength), truncatedMessage()] + } def "PBS amp should ignore and add a warning to ext.warnings when value of the request prefix is longer then settings.targeting.truncate-attr-chars"() { - given: "PBS config with setting.targeting" - def targetingChars = PBSUtils.getRandomNumber(2, 10) - def prebidServerService = pbsServiceFactory.getService( - ["settings.targeting.truncate-attr-chars": targetingChars as String]) - - and: "Default AmpRequest" + given: "Default AmpRequest" def ampRequest = AmpRequest.defaultAmpRequest and: "Bid request with prefix" - def prefix = PBSUtils.getRandomString(targetingChars) + def prefix = PBSUtils.getRandomString(DEFAULT_TRUNCATE_CHARS) def ampStoredRequest = BidRequest.defaultBidRequest.tap { - ext.prebid.targeting = new Targeting(prefix: PBSUtils.getRandomString(targetingChars)) + ext.prebid.targeting = new Targeting(prefix: prefix) } and: "Create and save stored request into DB" @@ -1042,66 +1058,51 @@ class TargetingSpec extends BaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp request" - def ampResponse = prebidServerService.sendAmpRequest(ampRequest) + def ampResponse = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp response should contain warning" - assert ampResponse.ext?.warnings[TARGETING]*.message == ["Key prefix value is dropped to default. " + - "Decrease custom prefix length or increase truncateattrchars by " + - "${prefix.length() + TARGETING_PREFIX_LENGTH - targetingChars}"] + def decreasePrefixLength = prefix.length() + TARGETING_PREFIX_LENGTH - DEFAULT_TRUNCATE_CHARS + assert ampResponse.ext?.warnings[TARGETING]*.message == [DROP_PREFIX_WARNING.formatted(decreasePrefixLength), truncatedMessage()] } def "PBS auction should ignore and add a warning to ext.warnings when value of the request prefix is longer then settings.targeting.truncate-attr-chars"() { - given: "PBS config with setting.targeting" - def targetingChars = PBSUtils.getRandomNumber(2, 10) - def prebidServerService = pbsServiceFactory.getService( - ["settings.targeting.truncate-attr-chars": targetingChars as String]) - - and: "Bid request with prefix" - def prefixSize = targetingChars + 1 + given: "Bid request with prefix" + def prefixSize = DEFAULT_TRUNCATE_CHARS + 1 def prefix = PBSUtils.getRandomString(prefixSize) def bidRequest = BidRequest.defaultBidRequest.tap { ext.prebid.targeting = new Targeting(prefix: prefix) } when: "PBS processes auction request" - def bidResponse = prebidServerService.sendAuctionRequest(bidRequest) + def bidResponse = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "Bid response should contain warning" def targeting = bidResponse.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting assert !targeting.isEmpty() assert targeting.keySet().every { it -> it.startsWith(DEFAULT_TARGETING_PREFIX) } - assert bidResponse.ext?.warnings[TARGETING]*.message == ["Key prefix value is dropped to default. " + - "Decrease custom prefix length or increase truncateattrchars by " + - "${prefix.length() + TARGETING_PREFIX_LENGTH - targetingChars}"] + assert bidResponse.ext?.warnings[TARGETING]*.message == [DROP_PREFIX_WARNING.formatted(prefix.length() + TARGETING_PREFIX_LENGTH - DEFAULT_TRUNCATE_CHARS)] } def "PBS auction should ignore and add a warning to ext.warnings when value of the account prefix is longer then settings.targeting.truncate-attr-chars"() { - given: "PBS config with setting.targeting" - def targetingChars = PBSUtils.getRandomNumber(2, 10) - def prebidServerService = pbsServiceFactory.getService( - ["settings.targeting.truncate-attr-chars": targetingChars as String]) - - and: "Bid request" + given: "Bid request" def bidRequest = BidRequest.defaultBidRequest.tap { ext.prebid.targeting = new Targeting() } and: "Account in the DB" - def prefix = PBSUtils.getRandomString(targetingChars + 1) + def prefix = PBSUtils.getRandomString(DEFAULT_TRUNCATE_CHARS + 1) def config = new AccountAuctionConfig(targeting: new Targeting(prefix: prefix)) def account = new Account(uuid: bidRequest.accountId, config: new AccountConfig(auction: config)) accountDao.save(account) when: "PBS processes auction request" - def bidResponse = prebidServerService.sendAuctionRequest(bidRequest) + def bidResponse = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "Bid response should contain warning" def targeting = bidResponse.seatbid?.first()?.bid?.first()?.ext?.prebid?.targeting assert !targeting.isEmpty() assert targeting.keySet().every { it -> it.startsWith(DEFAULT_TARGETING_PREFIX) } - assert bidResponse.ext?.warnings[TARGETING]*.message == ["Key prefix value is dropped to default. " + - "Decrease custom prefix length or increase truncateattrchars by " + - "${prefix.length() + TARGETING_PREFIX_LENGTH - targetingChars}"] + assert bidResponse.ext?.warnings[TARGETING]*.message == [DROP_PREFIX_WARNING.formatted(prefix.length() + TARGETING_PREFIX_LENGTH - DEFAULT_TRUNCATE_CHARS)] } def "PBS amp should apply data from query to ext.prebid.amp.data"() { @@ -1118,8 +1119,8 @@ class TargetingSpec extends BaseSpec { when: "PBS processes amp request" def unknownValue = PBSUtils.randomString def secondUnknownValue = PBSUtils.randomNumber - defaultPbsService.sendAmpRequestWithAdditionalQueries(ampRequest, ["unknown_field" : unknownValue, - "second_unknown_field": secondUnknownValue]) + pbsWithDefaultTargetingLength.sendAmpRequestWithAdditionalQueries(ampRequest, ["unknown_field" : unknownValue, + "second_unknown_field": secondUnknownValue]) then: "Amp should contain data from query request" def bidderRequests = bidder.getBidderRequest(ampStoredRequest.id) @@ -1140,7 +1141,7 @@ class TargetingSpec extends BaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp request" - def ampResponse = defaultPbsService.sendAmpRequest(ampRequest) + def ampResponse = pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Amp response should contain amp hb_env" def targeting = ampResponse.targeting @@ -1158,7 +1159,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes auction request" - defaultPbsService.sendAuctionRequest(bidRequest) + pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "Request should fail with an error" def exception = thrown(PrebidServerException) @@ -1180,7 +1181,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes auction request" - defaultPbsService.sendAuctionRequest(bidRequest) + pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "BidderRequest should include price granularity from bidRequest" def bidderRequest = bidder.getBidderRequest(bidRequest.id) @@ -1210,7 +1211,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes auction request" - defaultPbsService.sendAmpRequest(ampRequest) + pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "BidderRequest should include price granularity from bidRequest" def bidderRequest = bidder.getBidderRequest(ampStoredRequest.id) @@ -1231,7 +1232,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes auction request" - defaultPbsService.sendAuctionRequest(bidRequest) + pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "BidderRequest should include price granularity from account config" def bidderRequest = bidder.getBidderRequest(bidRequest.id) @@ -1252,7 +1253,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes auction request" - defaultPbsService.sendAuctionRequest(bidRequest) + pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "BidderRequest should include price granularity from account config" def bidderRequest = bidder.getBidderRequest(bidRequest.id) @@ -1263,12 +1264,14 @@ class TargetingSpec extends BaseSpec { } def "PBS auction should include price granularity from default account config when original request doesn't contain price granularity"() { - given: "Pbs with default account that include privacySandbox configuration" + given: "Default account that include privacySandbox configuration" def priceGranularity = PBSUtils.getRandomEnum(PriceGranularityType, [UNKNOWN]) def accountAuctionConfig = new AccountAuctionConfig(priceGranularity: priceGranularity) def accountConfig = new AccountConfig(status: ACTIVE, auction: accountAuctionConfig) - def pbsService = pbsServiceFactory.getService( - ["settings.default-account-config": encode(accountConfig)]) + + and: "PBS with default account" + def pbsConfig = ["settings.default-account-config": encode(accountConfig)] + def pbsService = pbsServiceFactory.getService(pbsConfig) and: "Default basic BidRequest" def bidRequest = BidRequest.defaultBidRequest.tap { @@ -1281,6 +1284,9 @@ class TargetingSpec extends BaseSpec { then: "BidderRequest should include price granularity from account config" def bidderRequest = bidder.getBidderRequest(bidRequest.id) assert bidderRequest?.ext?.prebid?.targeting?.priceGranularity == PriceGranularity.getDefault(priceGranularity) + + cleanup: "Stop and remove pbs container" + pbsServiceFactory.removeContainer(pbsConfig) } def "PBS auction should include include default price granularity when original request and account config doesn't contain price granularity"() { @@ -1295,7 +1301,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes auction request" - defaultPbsService.sendAuctionRequest(bidRequest) + pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "BidderRequest should include default price granularity" def bidderRequest = bidder.getBidderRequest(bidRequest.id) @@ -1327,7 +1333,7 @@ class TargetingSpec extends BaseSpec { accountDao.save(account) when: "PBS processes auction request" - defaultPbsService.sendAmpRequest(ampRequest) + pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "Request should fail with an error" def exception = thrown(PrebidServerException) @@ -1354,7 +1360,7 @@ class TargetingSpec extends BaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp request" - defaultPbsService.sendAmpRequest(ampRequest) + pbsWithDefaultTargetingLength.sendAmpRequest(ampRequest) then: "BidderRequest should include price granularity from account config" def bidderRequest = bidder.getBidderRequest(ampStoredRequest.id) @@ -1388,7 +1394,7 @@ class TargetingSpec extends BaseSpec { bidder.setResponse(bidRequest.id, bidResponse) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS bids in response shouldn't contain ranks" assert response?.seatbid?.bid?.ext?.prebid?.rank?.flatten() == [null] * MAX_BIDS_RANKING @@ -1433,7 +1439,7 @@ class TargetingSpec extends BaseSpec { bidder.setResponse(bidRequest.id, bidResponse) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS should rank single bid" verifyAll(response.seatbid.first.bid) { @@ -1473,7 +1479,7 @@ class TargetingSpec extends BaseSpec { bidder.setResponse(bidRequest.id, bidResponse) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS should rank single bid" verifyAll(response.seatbid.first.bid) { @@ -1514,7 +1520,7 @@ class TargetingSpec extends BaseSpec { bidder.setResponse(bidRequest.id, bidResponse) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS should rank bid with higher price as top priority" def bids = response.seatbid.first.bid @@ -1600,7 +1606,7 @@ class TargetingSpec extends BaseSpec { bidder.setResponse(bidRequest.id, bidResponse) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS should rank bid with higher price as top priority" def bids = response.seatbid.first.bid @@ -1702,7 +1708,7 @@ class TargetingSpec extends BaseSpec { bidder.setResponse(bidRequest.id, bidResponse) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS should rank bids for first imp" def bids = response.seatbid.first.bid @@ -1751,7 +1757,7 @@ class TargetingSpec extends BaseSpec { bidder.setResponse(bidRequest.id, bidResponse) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS should rank bid with higher price as top priority" def bids = response.seatbid.first.bid @@ -1802,7 +1808,7 @@ class TargetingSpec extends BaseSpec { bidder.setResponse(bidRequest.id, bidResponse) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS should rank bid with higher price as top priority" def bids = response.seatbid.first.bid @@ -1843,7 +1849,7 @@ class TargetingSpec extends BaseSpec { bidder.setResponse(bidRequest.id, bidResponse) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS bids in response shouldn't contain ranks" assert response?.seatbid?.bid?.ext?.prebid?.rank?.flatten() == [null] * MAX_BIDS_RANKING @@ -1882,7 +1888,7 @@ class TargetingSpec extends BaseSpec { storedResponseDao.save(storedResponse) when: "PBS processes auction request" - def response = defaultPbsService.sendAuctionRequest(bidRequest) + def response = pbsWithDefaultTargetingLength.sendAuctionRequest(bidRequest) then: "PBS should copy bid ranked from stored response" def bids = response.seatbid.first.bid @@ -1900,23 +1906,19 @@ class TargetingSpec extends BaseSpec { ] } - Account createAccountWithPriceGranularity(String accountId, PriceGranularityType priceGranularity) { + private static Account createAccountWithPriceGranularity(String accountId, PriceGranularityType priceGranularity) { def accountAuctionConfig = new AccountAuctionConfig(priceGranularity: priceGranularity) def accountConfig = new AccountConfig(status: ACTIVE, auction: accountAuctionConfig) new Account(uuid: accountId, config: accountConfig) } - Account getAccountConfigWithAuctionRanking(String accountId, Boolean auctionRankingEnablement = true) { + private static Account getAccountConfigWithAuctionRanking(String accountId, Boolean auctionRankingEnablement = true) { def accountAuctionConfig = new AccountAuctionConfig(ranking: new AccountRankingConfig(enabled: auctionRankingEnablement)) def accountConfig = new AccountConfig(status: ACTIVE, auction: accountAuctionConfig) new Account(uuid: accountId, config: accountConfig) } - private static PrebidServerService getEnabledWinBidsPbsService() { - pbsServiceFactory.getService(["auction.cache.only-winning-bids": "true"]) - } - - private static PrebidServerService getDisabledWinBidsPbsService() { - pbsServiceFactory.getService(["auction.cache.only-winning-bids": "false"]) + private static def truncatedMessage(List keys = ["hb_cache_host_${GENERIC}", "hb_cache_path_${GENERIC}"]) { + "$TRUNCATED_WARNING ${keys.join(', ')}" } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprSetUidSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprSetUidSpec.groovy index fa4c833baf2..df46f493775 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprSetUidSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprSetUidSpec.groovy @@ -58,7 +58,7 @@ class GdprSetUidSpec extends PrivacyBaseSpec { .build() } - and: "Default uids cookie with gener_x bidder" + and: "Default uids cookie with generic bidder" def uidsCookie = UidsCookie.defaultUidsCookie.tap { it.tempUIDs = [(GENERIC): defaultUidWithExpiry] } diff --git a/src/test/java/org/prebid/server/auction/CpmRangeTest.java b/src/test/java/org/prebid/server/auction/CpmRangeTest.java index 48028175363..c44ae1edd14 100644 --- a/src/test/java/org/prebid/server/auction/CpmRangeTest.java +++ b/src/test/java/org/prebid/server/auction/CpmRangeTest.java @@ -21,6 +21,20 @@ public class CpmRangeTest { + @Test + public void fromCpmShouldReturnZeroValueIfPriceDoesNotFitToRange() { + // given + final PriceGranularity priceGranularity = createFromExtPriceGranularity( + ExtPriceGranularity.of(null, singletonList(ExtGranularityRange.of(BigDecimal.valueOf(3), + BigDecimal.valueOf(0.01))))); + + // when + final String result = CpmRange.fromCpm(BigDecimal.valueOf(-2.0), priceGranularity, givenAccount()); + + // then + assertThat(result).isEqualTo("0.0"); + } + @Test public void fromCpmShouldReturnMaxRangeIfCpmExceedsIt() { assertThat(CpmRange.fromCpm(BigDecimal.valueOf(21), createFromString("auto"), givenAccount())) diff --git a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java index 748ce616e5c..20561c5b685 100644 --- a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java +++ b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java @@ -345,7 +345,7 @@ public void setUp() { given(fpdResolver.resolveImpExt(any(), anyBoolean())) .willAnswer(invocation -> invocation.getArgument(0)); - given(impAdjuster.adjust(any(), any(), any(), any())).willAnswer(invocation -> invocation.getArgument(0)); + given(impAdjuster.adjust(any(), any(), any())).willAnswer(invocation -> invocation.getArgument(0)); given(supplyChainResolver.resolveForBidder(anyString(), any())).willReturn(null); @@ -498,7 +498,7 @@ public void shouldExtractRequestWithBidderSpecificExtension() { final ObjectNode adjustedExt = givenImp.getExt().deepCopy(); final Imp adjustedImp = givenImp.toBuilder().ext(adjustedExt).build(); - given(impAdjuster.adjust(any(), any(), any(), any())).willReturn(adjustedImp); + given(impAdjuster.adjust(any(), any(), any())).willReturn(adjustedImp); // when target.holdAuction(givenRequestContext(bidRequest)); @@ -519,7 +519,7 @@ public void shouldExtractRequestWithBidderSpecificExtension() { .build()); final ArgumentCaptor impCaptor = forClass(Imp.class); - verify(impAdjuster).adjust(impCaptor.capture(), eq("someBidder"), any(), any()); + verify(impAdjuster).adjust(impCaptor.capture(), eq("someBidder"), any()); final Imp actualImp = impCaptor.getValue(); assertThat(actualImp).isNotSameAs(givenImp); diff --git a/src/test/java/org/prebid/server/auction/ImpAdjusterTest.java b/src/test/java/org/prebid/server/auction/ImpAdjusterTest.java index 9dc196f7884..e69a6169d77 100644 --- a/src/test/java/org/prebid/server/auction/ImpAdjusterTest.java +++ b/src/test/java/org/prebid/server/auction/ImpAdjusterTest.java @@ -12,8 +12,6 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.prebid.server.VertxTest; -import org.prebid.server.auction.aliases.BidderAliases; -import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.json.JsonMerger; import org.prebid.server.validation.ImpValidator; import org.prebid.server.validation.ValidationException; @@ -22,7 +20,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -35,18 +32,11 @@ public class ImpAdjusterTest extends VertxTest { @Mock private ImpValidator impValidator; - @Mock - private BidderCatalog bidderCatalog; - private ImpAdjuster target; - private BidderAliases bidderAliases; - @BeforeEach public void setUp() { target = new ImpAdjuster(jacksonMapper, new JsonMerger(jacksonMapper), impValidator); - bidderAliases = BidderAliases.of( - Map.of("someBidderAlias", "someBidder"), Collections.emptyMap(), bidderCatalog); } @Test @@ -56,7 +46,7 @@ public void adjustShouldReturnOriginalImpWhenImpExtPrebidImpIsNull() { final List debugMessages = new ArrayList<>(); // when - final Imp result = target.adjust(givenImp, "someBidder", bidderAliases, debugMessages); + final Imp result = target.adjust(givenImp, "someBidder", debugMessages); // then assertThat(result).isSameAs(givenImp); @@ -72,7 +62,7 @@ public void adjustShouldReturnOriginalImpWhenImpExtPrebidImpIsAbsent() { final List debugMessages = new ArrayList<>(); // when - final Imp result = target.adjust(givenImp, "someBidder", bidderAliases, debugMessages); + final Imp result = target.adjust(givenImp, "someBidder", debugMessages); // then assertThat(result).isSameAs(givenImp); @@ -90,7 +80,7 @@ public void adjustShouldSetImpExtIgsAeWhenImpExtAeIsZero() { final List debugMessages = new ArrayList<>(); // when - final Imp result = target.adjust(givenImp, "someBidder", bidderAliases, debugMessages); + final Imp result = target.adjust(givenImp, "someBidder", debugMessages); // then assertThat(result.getExt().get("igs").get("ae")).isEqualTo(IntNode.valueOf(0)); @@ -108,7 +98,7 @@ public void adjustShouldSetImpExtIgsAeWhenImpExtAeIsOne() { final List debugMessages = new ArrayList<>(); // when - final Imp result = target.adjust(givenImp, "someBidder", bidderAliases, debugMessages); + final Imp result = target.adjust(givenImp, "someBidder", debugMessages); // then assertThat(result.getExt().get("igs").get("ae")).isEqualTo(IntNode.valueOf(1)); @@ -126,7 +116,7 @@ public void adjustShouldNotSetImpExtIgsAeWhenImpExtAeIsNotZeroOrOne() { final List debugMessages = new ArrayList<>(); // when - final Imp result = target.adjust(givenImp, "someBidder", bidderAliases, debugMessages); + final Imp result = target.adjust(givenImp, "someBidder", debugMessages); // then assertThat(result.getExt().get("igs")).isNull(); @@ -145,7 +135,7 @@ public void adjustShouldNotModifyImpExtIgsAeWhenImpExtIgsAePresent() { final List debugMessages = new ArrayList<>(); // when - final Imp result = target.adjust(givenImp, "someBidder", bidderAliases, debugMessages); + final Imp result = target.adjust(givenImp, "someBidder", debugMessages); // then assertThat(result.getExt().get("igs").get("ae")).isEqualTo(IntNode.valueOf(123)); @@ -162,7 +152,7 @@ public void adjustShouldRemoveExpImpFromOriginalImpWhenImpExtPrebidImpHasEmptyBi final List debugMessages = new ArrayList<>(); // when - final Imp result = target.adjust(givenImp, "someBidder", bidderAliases, debugMessages); + final Imp result = target.adjust(givenImp, "someBidder", debugMessages); // then final Imp expectedImp = givenImp.toBuilder() @@ -186,7 +176,7 @@ public void resolveImpShouldMergeBidderSpecificImpIntoOriginalImp() throws Valid final List debugMessages = new ArrayList<>(); // when - final Imp result = target.adjust(givenImp, "someBidder", bidderAliases, debugMessages); + final Imp result = target.adjust(givenImp, "someBidder", debugMessages); // then final Imp expectedImp = givenImp.toBuilder() @@ -214,7 +204,7 @@ public void resolveImpShouldMergeBidderSpecificImpIntoOriginalImpCaseInsensitive final List debugMessages = new ArrayList<>(); // when - final Imp result = target.adjust(givenImp, "SOMEbiDDer", bidderAliases, debugMessages); + final Imp result = target.adjust(givenImp, "SOMEbiDDer", debugMessages); // then final Imp expectedImp = givenImp.toBuilder() @@ -238,11 +228,11 @@ public void resolveImpShouldMergeBidderSpecificImpIntoOriginalImpCaseAliasBidder .set("deals", mapper.createArrayNode() .add(mapper.createObjectNode().put("id", "dealId2")))); - final Imp givenImp = givenImp("someBidderAlias", givenBidderImp); + final Imp givenImp = givenImp("someBidder", givenBidderImp); final List debugMessages = new ArrayList<>(); // when - final Imp result = target.adjust(givenImp, "SOMEbiDDer", bidderAliases, debugMessages); + final Imp result = target.adjust(givenImp, "SOMEbiDDer", debugMessages); // then final Imp expectedImp = givenImp.toBuilder() @@ -272,7 +262,7 @@ public void resolveImpShouldReturnImpWithoutExpImpWhenResultingImpValidationFail final List debugMessages = new ArrayList<>(); // when - final Imp result = target.adjust(givenImp, "someBidder", bidderAliases, debugMessages); + final Imp result = target.adjust(givenImp, "someBidder", debugMessages); // then final Imp expectedImp = givenImp.toBuilder() @@ -297,7 +287,7 @@ public void resolveImpShouldReturnImpWithoutExpWhenMergingFailed() { final List debugMessages = new ArrayList<>(); // when - final Imp result = target.adjust(givenImp, "someBidder", bidderAliases, debugMessages); + final Imp result = target.adjust(givenImp, "someBidder", debugMessages); // then final Imp expectedImp = givenImp.toBuilder() diff --git a/src/test/java/org/prebid/server/auction/TargetingKeywordsCreatorTest.java b/src/test/java/org/prebid/server/auction/TargetingKeywordsCreatorTest.java index 49ab351205a..2d2799cca60 100644 --- a/src/test/java/org/prebid/server/auction/TargetingKeywordsCreatorTest.java +++ b/src/test/java/org/prebid/server/auction/TargetingKeywordsCreatorTest.java @@ -1,14 +1,18 @@ package org.prebid.server.auction; import com.iab.openrtb.response.Bid; +import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import org.prebid.server.proto.openrtb.ext.request.ExtGranularityRange; import org.prebid.server.proto.openrtb.ext.request.ExtPriceGranularity; +import org.prebid.server.proto.openrtb.ext.response.ExtBidderError; import org.prebid.server.settings.model.Account; import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; import java.util.Map; import static java.util.Collections.singletonList; @@ -46,7 +50,7 @@ public void shouldReturnTargetingKeywordsForOrdinaryBidOpenrtb() { null, null, defaultKeyPrefix) - .makeFor(bid, "bidder1", false, null, null, null, null, Account.empty("accountId")); + .makeFor(bid, "bidder1", false, null, null, null, null, Account.empty("accountId"), new HashMap<>()); // then assertThat(keywords).containsOnly( @@ -77,7 +81,8 @@ public void shouldReturnTargetingKeywordsWithEntireKeysOpenrtb() { null, null, defaultKeyPrefix) - .makeFor(bid, "veryververyverylongbidder1", false, null, null, null, null, Account.empty("accountId")); + .makeFor(bid, "veryververyverylongbidder1", false, null, null, + null, null, Account.empty("accountId"), new HashMap<>()); // then assertThat(keywords).containsOnly( @@ -113,7 +118,7 @@ public void shouldReturnTargetingKeywordsForWinningBidOpenrtb() { null, defaultKeyPrefix) .makeFor(bid, "bidder1", true, "cacheId1", "banner", - "videoCacheId1", "categoryDuration", Account.empty("accountId")); + "videoCacheId1", "categoryDuration", Account.empty("accountId"), new HashMap<>()); // then assertThat(keywords).containsOnly( @@ -156,7 +161,7 @@ public void shouldIncludeFormatOpenrtb() { null, null, defaultKeyPrefix) - .makeFor(bid, "", true, null, "banner", null, null, Account.empty("accountId")); + .makeFor(bid, "", true, null, "banner", null, null, Account.empty("accountId"), new HashMap<>()); // then assertThat(keywords).contains(entry("hb_format", "banner")); @@ -182,7 +187,7 @@ public void shouldNotIncludeCacheIdAndDealIdAndSizeOpenrtb() { null, null, defaultKeyPrefix) - .makeFor(bid, "bidder", true, null, null, null, null, Account.empty("accountId")); + .makeFor(bid, "bidder", true, null, null, null, null, Account.empty("accountId"), new HashMap<>()); // then assertThat(keywords).doesNotContainKeys("hb_cache_id_bidder", "hb_deal_bidder", "hb_size_bidder", @@ -209,7 +214,7 @@ public void shouldReturnEnvKeyForAppRequestOpenrtb() { null, null, defaultKeyPrefix) - .makeFor(bid, "bidder", true, null, null, null, null, Account.empty("accountId")); + .makeFor(bid, "bidder", true, null, null, null, null, Account.empty("accountId"), new HashMap<>()); // then assertThat(keywords).contains( @@ -237,7 +242,7 @@ public void shouldNotIncludeWinningBidTargetingIfIncludeWinnersFlagIsFalse() { null, null, defaultKeyPrefix) - .makeFor(bid, "bidder1", true, null, null, null, null, Account.empty("accountId")); + .makeFor(bid, "bidder1", true, null, null, null, null, Account.empty("accountId"), new HashMap<>()); // then assertThat(keywords).doesNotContainKeys("hb_bidder", "hb_pb"); @@ -263,7 +268,7 @@ public void shouldIncludeWinningBidTargetingIfIncludeWinnersFlagIsTrue() { null, null, defaultKeyPrefix) - .makeFor(bid, "bidder1", true, null, null, null, null, Account.empty("accountId")); + .makeFor(bid, "bidder1", true, null, null, null, null, Account.empty("accountId"), new HashMap<>()); // then assertThat(keywords).containsKeys("hb_bidder", "hb_pb"); @@ -289,7 +294,7 @@ public void shouldNotIncludeBidderKeysTargetingIfIncludeBidderKeysFlagIsFalse() null, null, defaultKeyPrefix) - .makeFor(bid, "bidder1", true, null, null, null, null, Account.empty("accountId")); + .makeFor(bid, "bidder1", true, null, null, null, null, Account.empty("accountId"), new HashMap<>()); // then assertThat(keywords).doesNotContainKeys("hb_bidder_bidder1", "hb_pb_bidder1"); @@ -315,7 +320,7 @@ public void shouldIncludeBidderKeysTargetingIfIncludeBidderKeysFlagIsTrue() { null, null, defaultKeyPrefix) - .makeFor(bid, "bidder1", true, null, null, null, null, Account.empty("accountId")); + .makeFor(bid, "bidder1", true, null, null, null, null, Account.empty("accountId"), new HashMap<>()); // then assertThat(keywords).containsKeys("hb_bidder_bidder1", "hb_pb_bidder1"); @@ -325,6 +330,7 @@ public void shouldIncludeBidderKeysTargetingIfIncludeBidderKeysFlagIsTrue() { public void shouldTruncateTargetingBidderKeywordsIfTruncateAttrCharsIsDefined() { // given final Bid bid = Bid.builder().price(BigDecimal.ONE).build(); + final Map> bidWarnings = new HashMap<>(); // when final Map keywords = TargetingKeywordsCreator.create( @@ -341,17 +347,24 @@ public void shouldTruncateTargetingBidderKeywordsIfTruncateAttrCharsIsDefined() null, null, defaultKeyPrefix) - .makeFor(bid, "someVeryLongBidderName", true, null, null, null, null, Account.empty("accountId")); + .makeFor(bid, "someVeryLongBidderName", true, null, null, + null, null, Account.empty("accountId"), bidWarnings); // then assertThat(keywords).hasSize(2) .containsKeys("hb_bidder_someVeryLo", "hb_pb_someVeryLongBi"); + assertThat(bidWarnings) + .extracting(warnings -> warnings.get("targeting")) + .asInstanceOf(InstanceOfAssertFactories.LIST) + .containsOnly(ExtBidderError.of(2, "The following keys have been truncated: " + + "hb_pb_someVeryLongBidderName, hb_bidder_someVeryLongBidderName")); } @Test public void shouldTruncateTargetingWithoutBidderSuffixKeywordsIfTruncateAttrCharsIsDefined() { // given final Bid bid = Bid.builder().price(BigDecimal.ONE).build(); + final Map> bidWarnings = new HashMap<>(); // when final Map keywords = TargetingKeywordsCreator.create( @@ -368,17 +381,23 @@ public void shouldTruncateTargetingWithoutBidderSuffixKeywordsIfTruncateAttrChar null, null, defaultKeyPrefix) - .makeFor(bid, "bidder", true, null, null, null, null, Account.empty("accountId")); + .makeFor(bid, "bidder", true, null, null, null, null, Account.empty("accountId"), bidWarnings); // then assertThat(keywords).hasSize(2) .containsKeys("hb_bidd", "hb_pb"); + + assertThat(bidWarnings) + .extracting(warnings -> warnings.get("targeting")) + .asInstanceOf(InstanceOfAssertFactories.LIST) + .containsOnly(ExtBidderError.of(2, "The following keys have been truncated: hb_bidder")); } @Test public void shouldTruncateTargetingAndDropDuplicatedWhenTruncateIsTooShort() { // given final Bid bid = Bid.builder().price(BigDecimal.ONE).build(); + final Map> bidWarnings = new HashMap<>(); // when final Map keywords = TargetingKeywordsCreator.create( @@ -395,18 +414,24 @@ public void shouldTruncateTargetingAndDropDuplicatedWhenTruncateIsTooShort() { null, null, defaultKeyPrefix) - .makeFor(bid, "bidder", true, null, null, null, null, Account.empty("accountId")); + .makeFor(bid, "bidder", true, null, null, null, null, Account.empty("accountId"), bidWarnings); // then - // Without truncating: "hb_bidder", "hb_bidder_bidder", "hb_env", "hb_env_bidder", "hb_pb", "hb_pb_bidder" assertThat(keywords).hasSize(4) .containsKeys("hb_bid", "hb_env", "hb_pb", "hb_pb_"); + + assertThat(bidWarnings) + .extracting(warnings -> warnings.get("targeting")) + .asInstanceOf(InstanceOfAssertFactories.LIST) + .containsOnly(ExtBidderError.of(2, "The following keys have been truncated: " + + "hb_pb_bidder, hb_bidder, hb_bidder_bidder, hb_env_bidder")); } @Test public void shouldNotTruncateTargetingKeywordsIfTruncateAttrCharsIsNotDefined() { // given final Bid bid = Bid.builder().price(BigDecimal.ONE).build(); + final Map> bidWarnings = new HashMap<>(); // when final Map keywords = TargetingKeywordsCreator.create( @@ -423,11 +448,13 @@ public void shouldNotTruncateTargetingKeywordsIfTruncateAttrCharsIsNotDefined() null, null, defaultKeyPrefix) - .makeFor(bid, "someVeryLongBidderName", true, null, null, null, null, Account.empty("accountId")); + .makeFor(bid, "someVeryLongBidderName", true, null, null, + null, null, Account.empty("accountId"), bidWarnings); // then assertThat(keywords).hasSize(2) .containsKeys("hb_bidder_someVeryLongBidderName", "hb_pb_someVeryLongBidderName"); + assertThat(bidWarnings).isEmpty(); } @Test @@ -440,6 +467,7 @@ public void shouldTruncateKeysFromResolver() { final TargetingKeywordsResolver resolver = mock(TargetingKeywordsResolver.class); given(resolver.resolve(any(), anyString())).willReturn(singletonMap("key_longer_than_twenty", "value1")); + final Map> bidWarnings = new HashMap<>(); // when final Map keywords = TargetingKeywordsCreator.create( @@ -456,10 +484,15 @@ public void shouldTruncateKeysFromResolver() { null, resolver, defaultKeyPrefix) - .makeFor(bid, "bidder1", true, null, null, null, null, Account.empty("accountId")); + .makeFor(bid, "bidder1", true, null, null, null, null, Account.empty("accountId"), bidWarnings); // then assertThat(keywords).contains(entry("key_longer_than_twen", "value1")); + + assertThat(bidWarnings) + .extracting(warnings -> warnings.get("targeting")) + .asInstanceOf(InstanceOfAssertFactories.LIST) + .containsOnly(ExtBidderError.of(2, "The following keys have been truncated: key_longer_than_twenty")); } @Test @@ -488,7 +521,7 @@ public void shouldIncludeKeywordsFromResolver() { null, resolver, defaultKeyPrefix) - .makeFor(bid, "bidder1", true, null, null, null, null, Account.empty("accountId")); + .makeFor(bid, "bidder1", true, null, null, null, null, Account.empty("accountId"), new HashMap<>()); // then assertThat(keywords).contains(entry("keyword1", "value1")); @@ -514,7 +547,7 @@ public void shouldIncludeDealBidTargetingIfAlwaysIncludeDealsFlagIsTrue() { null, null, defaultKeyPrefix) - .makeFor(bid, "bidder1", false, null, null, null, null, Account.empty("accountId")); + .makeFor(bid, "bidder1", false, null, null, null, null, Account.empty("accountId"), new HashMap<>()); // then assertThat(keywords).containsOnlyKeys("hb_bidder_bidder1", "hb_deal_bidder1", "hb_pb_bidder1"); @@ -540,7 +573,7 @@ public void shouldNotIncludeDealBidTargetingIfAlwaysIncludeDealsFlagIsFalse() { null, null, defaultKeyPrefix) - .makeFor(bid, "bidder1", false, null, null, null, null, Account.empty("accountId")); + .makeFor(bid, "bidder1", false, null, null, null, null, Account.empty("accountId"), new HashMap<>()); // then assertThat(keywords).doesNotContainKeys("hb_bidder_bidder1", "hb_deal_bidder1", "hb_pb_bidder1");