From b794adef0f6cd7c3349b79b28ffef9d55d9f4bc4 Mon Sep 17 00:00:00 2001 From: antonbabak Date: Fri, 12 Sep 2025 13:48:34 +0200 Subject: [PATCH 1/3] Add a warning for when targeting attributes are truncated --- .../server/auction/BidResponseCreator.java | 4 +- .../auction/TargetingKeywordsCreator.java | 47 ++++++++---- .../auction/TargetingKeywordsCreatorTest.java | 71 ++++++++++++++----- 3 files changed, 88 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/prebid/server/auction/BidResponseCreator.java b/src/main/java/org/prebid/server/auction/BidResponseCreator.java index aec0c82831b..bcb91f88219 100644 --- a/src/main/java/org/prebid/server/auction/BidResponseCreator.java +++ b/src/main/java/org/prebid/server/auction/BidResponseCreator.java @@ -1564,8 +1564,8 @@ private Bid toBid(BidInfo bidInfo, final String seat = targetingInfo.getSeat(); final String categoryDuration = bidInfo.getCategory(); targetingKeywords = keywordsCreator != null - ? keywordsCreator.makeFor( - bid, seat, isWinningBid, cacheId, bidType.getName(), videoCacheId, categoryDuration, account) + ? keywordsCreator.makeFor(bid, seat, isWinningBid, cacheId, bidType.getName(), + videoCacheId, categoryDuration, account, bidWarnings) : null; } else { targetingKeywords = null; diff --git a/src/main/java/org/prebid/server/auction/TargetingKeywordsCreator.java b/src/main/java/org/prebid/server/auction/TargetingKeywordsCreator.java index b873210c1f3..5d8eac8d184 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; @@ -154,7 +156,8 @@ Map makeFor(Bid bid, String format, String vastCacheId, String categoryDuration, - Account account) { + Account account, + Map> bidWarnings) { final Map keywords = makeFor( bidder, @@ -170,13 +173,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); } /** @@ -261,18 +264,36 @@ 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) { + final List truncatedKeys = new ArrayList<>(); + final Map keys = keyValues.entrySet().stream().collect(Collectors.toMap( + keyValue -> truncateKey(keyValue.getKey(), truncatedKeys), + Map.Entry::getValue, + (key1, key2) -> key1)); + + 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; + } + + return keyValues; } - private String truncateKey(String key) { - return key.length() > truncateAttrChars - ? key.substring(0, truncateAttrChars) - : key; + private String truncateKey(String key, List truncatedKeys) { + if (key.length() > truncateAttrChars) { + truncatedKeys.add(key); + return key.substring(0, truncateAttrChars); + } + + return key; } /** 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"); From 3913931d70e2991eaabfbb8be4d7fceb51467d50 Mon Sep 17 00:00:00 2001 From: antonbabak Date: Wed, 17 Sep 2025 11:25:23 +0200 Subject: [PATCH 2/3] Fix comments --- .../server/auction/BidResponseCreator.java | 12 ++++- .../auction/TargetingKeywordsCreator.java | 46 ++++++++++--------- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/prebid/server/auction/BidResponseCreator.java b/src/main/java/org/prebid/server/auction/BidResponseCreator.java index bcb91f88219..8e1f06c736a 100644 --- a/src/main/java/org/prebid/server/auction/BidResponseCreator.java +++ b/src/main/java/org/prebid/server/auction/BidResponseCreator.java @@ -1564,8 +1564,16 @@ private Bid toBid(BidInfo bidInfo, final String seat = targetingInfo.getSeat(); final String categoryDuration = bidInfo.getCategory(); targetingKeywords = keywordsCreator != null - ? keywordsCreator.makeFor(bid, seat, isWinningBid, cacheId, bidType.getName(), - videoCacheId, categoryDuration, account, bidWarnings) + ? keywordsCreator.makeFor( + bid, + seat, + isWinningBid, + cacheId, + bidType.getName(), + videoCacheId, + categoryDuration, + account, + bidWarnings) : null; } else { targetingKeywords = null; diff --git a/src/main/java/org/prebid/server/auction/TargetingKeywordsCreator.java b/src/main/java/org/prebid/server/auction/TargetingKeywordsCreator.java index 5d8eac8d184..30507e4072c 100644 --- a/src/main/java/org/prebid/server/auction/TargetingKeywordsCreator.java +++ b/src/main/java/org/prebid/server/auction/TargetingKeywordsCreator.java @@ -14,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. @@ -267,33 +266,36 @@ private static String sizeFrom(Integer width, Integer height) { private Map truncateKeys(Map keyValues, Map> bidWarnings) { - if (truncateAttrChars > 0) { - final List truncatedKeys = new ArrayList<>(); - final Map keys = keyValues.entrySet().stream().collect(Collectors.toMap( - keyValue -> truncateKey(keyValue.getKey(), truncatedKeys), - Map.Entry::getValue, - (key1, key2) -> key1)); - - 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)); + 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); } + } - return keys; + 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 keyValues; + return keys; } - private String truncateKey(String key, List truncatedKeys) { - if (key.length() > truncateAttrChars) { - truncatedKeys.add(key); - return key.substring(0, truncateAttrChars); - } - - return key; + private String truncateKey(String key) { + return key.length() > truncateAttrChars + ? key.substring(0, truncateAttrChars) + : key; } /** From effec4937cdf622620f6906e0d99409d441c69b3 Mon Sep 17 00:00:00 2001 From: osulzhenko <125548596+osulzhenko@users.noreply.github.com> Date: Mon, 29 Sep 2025 12:46:46 +0300 Subject: [PATCH 3/3] Test: Add a warning for when targeting attributes are truncated (#4192) --- .../testcontainers/PbsConfig.groovy | 4 + .../container/PrebidServerContainer.groovy | 1 + .../server/functional/tests/CacheSpec.groovy | 9 +- .../functional/tests/TargetingSpec.groovy | 265 ++++++++---------- 4 files changed, 130 insertions(+), 149 deletions(-) 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/TargetingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/TargetingSpec.groovy index 960e05b458e..dc0607e5e31 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,28 @@ 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 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 +101,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 +127,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 +152,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 +180,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 +207,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 +231,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 +253,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 +282,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 +344,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 +376,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 +410,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 +424,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 +441,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 +466,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 +488,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 +518,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 +528,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,13 +551,13 @@ 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 @@ -551,7 +572,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 +593,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 +608,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 +631,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 +650,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 +671,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 +691,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 +713,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 +739,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 +764,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 +789,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 +818,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 +841,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 +869,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 +899,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 +907,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 +918,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 +933,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 +947,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 +956,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 +972,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 +993,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 +1004,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 +1033,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 +1094,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 +1116,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 +1134,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 +1156,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 +1186,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 +1207,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 +1228,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 +1239,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 +1259,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 +1276,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 +1308,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 +1335,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 +1369,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 +1414,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 +1454,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 +1495,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 +1581,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 +1683,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 +1732,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 +1783,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 +1824,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 +1863,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 +1881,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(', ')}" } }