From 877b29c7b771004a3455dc742a2d465d5cdf17a1 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Wed, 30 Apr 2025 19:24:20 +0300 Subject: [PATCH 1/9] Tests: fix alert.general metric conflict --- .../prebid/server/functional/model/privacy/Metric.groovy | 1 - .../tests/privacy/GppFetchBidActivitiesSpec.groovy | 9 ++++----- .../tests/privacy/GppSyncUserActivitiesSpec.groovy | 9 ++++----- .../tests/privacy/GppTransmitEidsActivitiesSpec.groovy | 9 ++++----- .../privacy/GppTransmitPreciseGeoActivitiesSpec.groovy | 9 ++++----- .../tests/privacy/GppTransmitUfpdActivitiesSpec.groovy | 9 ++++----- 6 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/model/privacy/Metric.groovy b/src/test/groovy/org/prebid/server/functional/model/privacy/Metric.groovy index 490c1456e53..80d779e069d 100644 --- a/src/test/groovy/org/prebid/server/functional/model/privacy/Metric.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/privacy/Metric.groovy @@ -7,7 +7,6 @@ import org.prebid.server.functional.model.request.setuid.SetuidRequest enum Metric { - ALERT_GENERAL("alerts.general"), PROCESSED_ACTIVITY_RULES_COUNT("requests.activity.processedrules.count"), ACCOUNT_PROCESSED_RULES_COUNT("requests.activity.processedrules.count"), TEMPLATE_ADAPTER_DISALLOWED_COUNT("adapter.{bidderName}.activity.{activityType}.disallowed.count"), diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppFetchBidActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppFetchBidActivitiesSpec.groovy index 46634fda48f..2dd15697102 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppFetchBidActivitiesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppFetchBidActivitiesSpec.groovy @@ -60,7 +60,6 @@ import static org.prebid.server.functional.model.pricefloors.Country.USA import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ACCOUNT_DISALLOWED_COUNT import static org.prebid.server.functional.model.privacy.Metric.ACCOUNT_PROCESSED_RULES_COUNT import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ADAPTER_DISALLOWED_COUNT -import static org.prebid.server.functional.model.privacy.Metric.ALERT_GENERAL import static org.prebid.server.functional.model.privacy.Metric.PROCESSED_ACTIVITY_RULES_COUNT import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_REQUEST_DISALLOWED_COUNT import static org.prebid.server.functional.model.request.GppSectionId.USP_V1 @@ -694,7 +693,7 @@ class GppFetchBidActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 where: gppAccountsConfig << [[new AccountGppConfig(code: IAB_US_GENERAL, config: new GppModuleConfig(skipSids: [US_NAT_V1]), enabled: false), @@ -868,7 +867,7 @@ class GppFetchBidActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS auction call when custom privacy regulation with normalizing should ignore call to bidder"() { @@ -1425,7 +1424,7 @@ class GppFetchBidActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS amp call when privacy module contain invalid property should respond with an error"() { @@ -1624,7 +1623,7 @@ class GppFetchBidActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS amp call when custom privacy regulation with normalizing should ignore call to bidder"() { diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppSyncUserActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppSyncUserActivitiesSpec.groovy index 657e0fef1e5..973d6bde700 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppSyncUserActivitiesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppSyncUserActivitiesSpec.groovy @@ -59,7 +59,6 @@ import static org.prebid.server.functional.model.config.UsNationalPrivacySection import static org.prebid.server.functional.model.pricefloors.Country.CAN import static org.prebid.server.functional.model.pricefloors.Country.USA import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ADAPTER_DISALLOWED_COUNT -import static org.prebid.server.functional.model.privacy.Metric.ALERT_GENERAL import static org.prebid.server.functional.model.privacy.Metric.PROCESSED_ACTIVITY_RULES_COUNT import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_REQUEST_DISALLOWED_COUNT import static org.prebid.server.functional.model.request.GppSectionId.USP_V1 @@ -657,7 +656,7 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS cookie sync call when privacy module contain invalid code should include proper responded with bidders URLs"() { @@ -817,7 +816,7 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS cookie sync when custom privacy regulation with normalizing should exclude bidders URLs"() { @@ -1550,7 +1549,7 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS setuid request call when privacy module contain invalid code should respond with valid bidders UIDs cookies"() { @@ -1725,7 +1724,7 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS setuid call when custom privacy regulation with normalizing should reject bidders with status code invalidStatusCode"() { diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitEidsActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitEidsActivitiesSpec.groovy index edd720700ca..0efdb70fd7d 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitEidsActivitiesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitEidsActivitiesSpec.groovy @@ -59,7 +59,6 @@ import static org.prebid.server.functional.model.pricefloors.Country.USA import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ACCOUNT_DISALLOWED_COUNT import static org.prebid.server.functional.model.privacy.Metric.ACCOUNT_PROCESSED_RULES_COUNT import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ADAPTER_DISALLOWED_COUNT -import static org.prebid.server.functional.model.privacy.Metric.ALERT_GENERAL import static org.prebid.server.functional.model.privacy.Metric.PROCESSED_ACTIVITY_RULES_COUNT import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_REQUEST_DISALLOWED_COUNT import static org.prebid.server.functional.model.request.GppSectionId.USP_V1 @@ -1023,7 +1022,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS auction call when privacy module contain invalid property should respond with an error"() { @@ -1185,7 +1184,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS auction call when custom privacy regulation with normalizing that match custom config should have empty EIDS fields"() { @@ -2097,7 +2096,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS amp call when privacy module contain invalid property should respond with an error"() { @@ -2295,7 +2294,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS amp call when custom privacy regulation with normalizing should change request consent and call to bidder"() { diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitPreciseGeoActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitPreciseGeoActivitiesSpec.groovy index c0c9495bfc3..4707633d01d 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitPreciseGeoActivitiesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitPreciseGeoActivitiesSpec.groovy @@ -58,7 +58,6 @@ import static org.prebid.server.functional.model.pricefloors.Country.USA import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ACCOUNT_DISALLOWED_COUNT import static org.prebid.server.functional.model.privacy.Metric.ACCOUNT_PROCESSED_RULES_COUNT import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ADAPTER_DISALLOWED_COUNT -import static org.prebid.server.functional.model.privacy.Metric.ALERT_GENERAL import static org.prebid.server.functional.model.privacy.Metric.PROCESSED_ACTIVITY_RULES_COUNT import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_REQUEST_DISALLOWED_COUNT import static org.prebid.server.functional.model.request.GppSectionId.USP_V1 @@ -1359,7 +1358,7 @@ class GppTransmitPreciseGeoActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS auction call when privacy module contain invalid code should respond with an error"() { @@ -1573,7 +1572,7 @@ class GppTransmitPreciseGeoActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS auction call when custom privacy regulation with normalizing should change request consent and call to bidder"() { @@ -2613,7 +2612,7 @@ class GppTransmitPreciseGeoActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS amp call when privacy module contain invalid code should respond with an error"() { @@ -2863,7 +2862,7 @@ class GppTransmitPreciseGeoActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS amp call when custom privacy regulation with normalizing should change request consent and call to bidder"() { diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitUfpdActivitiesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitUfpdActivitiesSpec.groovy index 0eaafecc6f0..d33681cc395 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitUfpdActivitiesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/GppTransmitUfpdActivitiesSpec.groovy @@ -63,7 +63,6 @@ import static org.prebid.server.functional.model.pricefloors.Country.USA import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ACCOUNT_DISALLOWED_COUNT import static org.prebid.server.functional.model.privacy.Metric.ACCOUNT_PROCESSED_RULES_COUNT import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ADAPTER_DISALLOWED_COUNT -import static org.prebid.server.functional.model.privacy.Metric.ALERT_GENERAL import static org.prebid.server.functional.model.privacy.Metric.PROCESSED_ACTIVITY_RULES_COUNT import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_REQUEST_DISALLOWED_COUNT import static org.prebid.server.functional.model.request.GppSectionId.USP_V1 @@ -1378,7 +1377,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS auction call when privacy module contain invalid property should respond with an error"() { @@ -1573,7 +1572,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS auction call when custom privacy regulation with normalizing that match custom config should have empty UFPD fields"() { @@ -2748,7 +2747,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS amp call when privacy module contain invalid property should respond with an error"() { @@ -2979,7 +2978,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL.getValue()] == 1 + assert metrics[ALERT_GENERAL] == 1 } def "PBS amp call when custom privacy regulation with normalizing should change request consent and call to bidder"() { From 52e11d662da1b62a9da27947929d61453bee6c80 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Sun, 4 May 2025 19:31:27 +0300 Subject: [PATCH 2/9] Tests: Increase coverage for floors logs tests --- .../model/response/auction/Bid.groovy | 2 +- .../pricefloors/PriceFloorsBaseSpec.groovy | 20 ++ .../PriceFloorsFetchingSpec.groovy | 260 +++++++++++++++-- .../PriceFloorsSignalingSpec.groovy | 262 ++++++++---------- .../server/functional/util/PBSUtils.groovy | 4 + 5 files changed, 375 insertions(+), 173 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/Bid.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/Bid.groovy index 22e29b76908..be764c64df4 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/Bid.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/Bid.groovy @@ -60,7 +60,7 @@ class Bid implements ObjectMapperWrapper { new Bid().tap { id = UUID.randomUUID() impid = imp.id - price = PBSUtils.getRandomPrice() + price = imp.bidFloor != null ? PBSUtils.getRandomPrice(imp.bidFloor) : PBSUtils.getRandomPrice() crid = 1 height = imp.banner && imp.banner.format ? imp.banner.format.first().height : null weight = imp.banner && imp.banner.format ? imp.banner.format.first().weight : null diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsBaseSpec.groovy index 4e1cbbf349b..a8b18661abf 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsBaseSpec.groovy @@ -47,6 +47,26 @@ abstract class PriceFloorsBaseSpec extends BaseSpec { protected static final FloorsProvider floorsProvider = new FloorsProvider(networkServiceContainer) protected final PrebidServerService floorsPbsService = pbsServiceFactory.getService(FLOORS_CONFIG + GENERIC_ALIAS_CONFIG) + protected static final Closure INVALID_CONFIG_METRIC = { account -> "alerts.account_config.${account}.price-floors" } + + protected static final Closure URL_EMPTY_ERROR = { url -> "Failed to fetch price floor from provider for fetch.url '${url}'" } + protected static final Closure URL_EMPTY_WARNING_MESSAGE = { url, message -> + "${URL_EMPTY_ERROR(url)}, with a reason: $message" + } + protected static final Closure URL_EMPTY_ERROR_LOG = { bidRequest, message -> + "No price floor data for account ${bidRequest.accountId} and " + + "request ${bidRequest.id}, reason: ${URL_EMPTY_WARNING_MESSAGE("$BASIC_FETCH_URL$bidRequest.accountId", message)}" + } + // Required: Sync no longer logs "in progress"—only "none" or "error" statuses are recorded + protected static final String FETCHING_DISABLED_ERROR = 'Fetching is disabled' + protected static final Closure FETCHING_DISABLED_WARNING_MESSAGE = { message -> + "$FETCHING_DISABLED_ERROR. Following parsing of request price floors is failed: $message" + } + protected static final Closure FETCHING_DISABLED_ERROR_LOG = { bidRequest, message -> + "No price floor data for account ${bidRequest.accountId} and " + + "request ${bidRequest.id}, reason: ${FETCHING_DISABLED_WARNING_MESSAGE(message)}" + } + def setupSpec() { floorsProvider.setResponse() } diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy index 699b9bdcda0..767445c04ce 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy @@ -1,6 +1,10 @@ package org.prebid.server.functional.tests.pricefloors +import org.prebid.server.functional.model.config.AccountAuctionConfig +import org.prebid.server.functional.model.config.AccountConfig +import org.prebid.server.functional.model.config.AccountPriceFloorsConfig import org.prebid.server.functional.model.config.PriceFloorsFetch +import org.prebid.server.functional.model.db.Account import org.prebid.server.functional.model.db.StoredRequest import org.prebid.server.functional.model.pricefloors.ModelGroup import org.prebid.server.functional.model.pricefloors.PriceFloorData @@ -23,6 +27,7 @@ import static org.prebid.server.functional.model.pricefloors.MediaType.BANNER import static org.prebid.server.functional.model.request.auction.DistributionChannel.APP import static org.prebid.server.functional.model.request.auction.DistributionChannel.SITE import static org.prebid.server.functional.model.request.auction.FetchStatus.ERROR +import static org.prebid.server.functional.model.request.auction.FetchStatus.INPROGRESS import static org.prebid.server.functional.model.request.auction.FetchStatus.NONE import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS import static org.prebid.server.functional.model.request.auction.Location.FETCH @@ -45,22 +50,13 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { private static final int FLOOR_MIN = 0 private static final String FETCH_FAILURE_METRIC = "price-floors.fetch.failure" - private static final String FETCHING_DISABLED_ERROR = 'Fetching is disabled' private static final String PRICE_FLOOR_VALUES_MISSING = 'Price floor rules values can\'t be null or empty, but were null' private static final String MODEL_WEIGHT_INVALID = "Price floor modelGroup modelWeight must be in range(1-100), but was %s" private static final String SKIP_RATE_INVALID = "Price floor modelGroup skipRate must be in range(0-100), but was %s" + private static Instant startTime - private static final Closure INVALID_CONFIG_METRIC = { account -> "alerts.account_config.${account}.price-floors" } - private static final Closure URL_EMPTY_ERROR = { url -> "Failed to fetch price floor from provider for fetch.url '${url}'" } - private static final Closure URL_EMPTY_WARNING_MESSAGE = { url, message -> - "${URL_EMPTY_ERROR(url)}, with a reason: $message" - } - private static final Closure URL_EMPTY_ERROR_LOG = { bidRequest, message -> - "No price floor data for account ${bidRequest.accountId} and " + - "request ${bidRequest.id}, reason: ${URL_EMPTY_WARNING_MESSAGE("$BASIC_FETCH_URL$bidRequest.accountId", message)}" - } - private static final Closure WARNING_MESSAGE = { message -> - "$FETCHING_DISABLED_ERROR. Following parsing of request price floors is failed: $message" + def setupSpec() { + startTime = Instant.now() } def "PBS should activate floors feature when price-floors.enabled = true in PBS config"() { @@ -1309,6 +1305,10 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { } accountDao.save(account) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + when: "PBS processes auction request" def response = floorsPbsService.sendAuctionRequest(bidRequest) @@ -1319,7 +1319,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" def message = "Price floor floorMin must be positive float, but was $invalidFloorMin" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] } def "PBS should validate rules from request when request doesn't contain modelGroups"() { @@ -1336,6 +1336,10 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { } accountDao.save(account) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + when: "PBS processes auction request" def response = floorsPbsService.sendAuctionRequest(bidRequest) @@ -1346,7 +1350,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" def message = "Price floor rules should contain at least one model group" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] } def "PBS should validate rules from request when request doesn't contain values"() { @@ -1363,6 +1367,10 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { } accountDao.save(account) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + when: "PBS processes auction request" def response = floorsPbsService.sendAuctionRequest(bidRequest) @@ -1372,7 +1380,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(PRICE_FLOOR_VALUES_MISSING)] + assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(PRICE_FLOOR_VALUES_MISSING)] } def "PBS should validate rules from request when modelWeight from request is invalid"() { @@ -1393,6 +1401,10 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { } accountDao.save(account) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + when: "PBS processes auction request" def response = floorsPbsService.sendAuctionRequest(bidRequest) @@ -1402,7 +1414,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(MODEL_WEIGHT_INVALID.formatted(invalidModelWeight))] + assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(MODEL_WEIGHT_INVALID.formatted(invalidModelWeight))] where: invalidModelWeight << [0, MAX_MODEL_WEIGHT + 1] @@ -1431,6 +1443,10 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { } accountDao.save(account) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(ampStoredRequest) + bidder.setResponse(ampStoredRequest.id, bidResponse) + when: "PBS processes auction request" def response = floorsPbsService.sendAmpRequest(ampRequest) @@ -1440,7 +1456,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(MODEL_WEIGHT_INVALID.formatted(invalidModelWeight))] + assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(MODEL_WEIGHT_INVALID.formatted(invalidModelWeight))] where: invalidModelWeight << [0, MAX_MODEL_WEIGHT + 1] @@ -1469,6 +1485,10 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "PBS fetch rules from floors provider" cacheFloorsProviderRules(bidRequest) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + when: "PBS processes auction request" def response = floorsPbsService.sendAuctionRequest(bidRequest) @@ -1479,7 +1499,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" def message = "Price floor root skipRate must be in range(0-100), but was $invalidSkipRate" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] where: invalidSkipRate << [SKIP_RATE_MIN - 1, SKIP_RATE_MAX + 1] @@ -1508,6 +1528,10 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "PBS fetch rules from floors provider" cacheFloorsProviderRules(bidRequest) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + when: "PBS processes auction request" def response = floorsPbsService.sendAuctionRequest(bidRequest) @@ -1518,7 +1542,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" def message = "Price floor data skipRate must be in range(0-100), but was $invalidSkipRate" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] where: invalidSkipRate << [SKIP_RATE_MIN - 1, SKIP_RATE_MAX + 1] @@ -1547,6 +1571,10 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "PBS fetch rules from floors provider" cacheFloorsProviderRules(bidRequest) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + when: "PBS processes auction request" def response = floorsPbsService.sendAuctionRequest(bidRequest) @@ -1556,7 +1584,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(SKIP_RATE_INVALID.formatted(invalidSkipRate))] + assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(SKIP_RATE_INVALID.formatted(invalidSkipRate))] where: invalidSkipRate << [SKIP_RATE_MIN - 1, SKIP_RATE_MAX + 1] @@ -1581,6 +1609,10 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { } accountDao.save(account) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + when: "PBS processes auction request" def response = floorsPbsService.sendAuctionRequest(bidRequest) @@ -1591,7 +1623,193 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" def message = "Price floor modelGroup default must be positive float, but was $invalidDefaultFloorValue" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + } + + def "PBS shouldn't emit error in log and response when floors is not in request and floors fetching disabled for account"() { + given: "Account with disabled fetching" + def priceFloors = new AccountPriceFloorsConfig(enabled: false, fetch: new PriceFloorsFetch(enabled: false)) + def accountAuctionConfig = new AccountAuctionConfig(priceFloors: priceFloors) + def accountConfig = new AccountConfig(auction: accountAuctionConfig) + def account = new Account(uuid: bidRequest.accountId, config: accountConfig) + accountDao.save(account) + + and: "Flush metrics" + flushMetrics(floorsPbsService) + + and: "Default bid response" + def response = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, response) + + when: "PBS processes auction request" + def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) + + then: "PBS shouldn't log warning or errors" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS shouldn't log a errors" + def message = 'Price floor rules data must be present' + def logs = floorsPbsService.getLogsByTime(startTime) + def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + assert !floorsLogs.size() + + and: "PBS request on response object status should be in progress" + assert getRequests(bidResponse)[GENERIC.value]?.first?.ext?.prebid?.floors?.fetchStatus == INPROGRESS + + and: "PBS bidderRequest status should be in progress" + def bidderRequest = bidder.getBidderRequests(bidRequest.id) + assert bidderRequest.ext.prebid.floors.fetchStatus == [INPROGRESS] + + and: "Alerts.general metrics shouldn't be populated" + def metrics = floorsPbsService.sendCollectedMetricsRequest() + assert !metrics[ALERT_GENERAL] + + where: + bidRequest << [BidRequest.getDefaultBidRequest(), getBidRequestWithFloors().tap { it.ext.prebid.floors = null }] + } + + def "PBS should emit error in log and response when data is invalid and floors fetching disabled for account and #requestFloors for request"() { + given: "Default BidRequest with empty floors.data" + def bidRequest = bidRequestWithFloors.tap { + ext.prebid.floors.enabled = requestFloors + ext.prebid.floors.data = null + } + + and: "Flush metrics" + flushMetrics(floorsPbsService) + + and: "Account with disabled fetching" + def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { + config.auction.priceFloors.fetch.enabled = false + } + accountDao.save(account) + + and: "Default bid response" + def response = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, response) + + when: "PBS processes auction request" + def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) + + then: "PBS should log a warning" + def message = 'Price floor rules data must be present' + assert bidResponse.ext?.warnings[PREBID]*.code == [999] + assert bidResponse.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + + and: "PBS should not add errors" + assert !bidResponse.ext.errors + + and: "PBS should log a errors" + def logs = floorsPbsService.getLogsByTime(startTime) + def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + assert floorsLogs.size() == 1 + + and: "PBS request on response object status should be not in progress" + assert getRequests(bidResponse)[GENERIC.value]?.first?.ext?.prebid?.floors?.fetchStatus == NONE + + and: "PBS request status shouldn't be in progress" + def bidderRequest = bidder.getBidderRequests(bidRequest.id) + assert bidderRequest.ext.prebid.floors.fetchStatus == [NONE] + + and: "Alerts.general metrics should be populated" + def metrics = floorsPbsService.sendCollectedMetricsRequest() + assert metrics[ALERT_GENERAL] == 1 + + where: + requestFloors << [null, true] + } + + def "PBS shouldn't emit error in log and response when data is invalid and floors fetching enabled for account"() { + given: "Default BidRequest with empty floors.data" + def bidRequest = bidRequestWithFloors.tap { + ext.prebid.floors.enabled = requestFloors + ext.prebid.floors.data = null + } + + and: "Account with enabled fetching" + def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { + config.auction.priceFloors.enabled = true + config.auction.priceFloors.fetch.enabled = true + } + accountDao.save(account) + + and: "Flush metrics" + flushMetrics(floorsPbsService) + + and: "Default bid response" + def response = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, response) + + when: "PBS processes auction request" + def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) + + then: "PBS shouldn't log warning or errors" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS shouldn't log a errors" + def message = 'Price floor rules data must be present' + def logs = floorsPbsService.getLogsByTime(startTime) + def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + assert !floorsLogs.size() + + and: "PBS request on response object status should be in progress" + assert getRequests(bidResponse)[GENERIC.value]?.first?.ext?.prebid?.floors?.fetchStatus == INPROGRESS + + and: "PBS bidderRequest status should be in progress" + def bidderRequest = bidder.getBidderRequests(bidRequest.id) + assert bidderRequest.ext.prebid.floors.fetchStatus == [INPROGRESS] + + and: "Alerts.general metrics shouldn't be populated" + def metrics = floorsPbsService.sendCollectedMetricsRequest() + assert !metrics[ALERT_GENERAL] + + where: + requestFloors << [null, true] + } + + def "PBS shouldn't emit error in log and response when data is invalid and floors disabled for request"() { + given: "Default BidRequest with empty floors.data" + def bidRequest = bidRequestWithFloors.tap { + ext.prebid.floors.enabled = false + ext.prebid.floors.data = null + } + + and: "Flush metrics" + flushMetrics(floorsPbsService) + + and: "Account with disabled fetching" + def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { + config.auction.priceFloors.enabled = false + config.auction.priceFloors.fetch.enabled = false + } + accountDao.save(account) + + and: "PBS fetch rules from floors provider" + cacheFloorsProviderRules(bidRequest, floorsPbsService) + + and: "Default bid response" + def response = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, response) + + when: "PBS processes auction request" + def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) + + then: "PBS should not add warning or errors" + assert !bidResponse.ext.warnings + assert !bidResponse.ext.errors + + and: "PBS request on response object status should be empty" + assert getRequests(bidResponse)[GENERIC.value]?.first?.ext?.prebid?.floors?.fetchStatus == null + + and: "PBS request status should be empty" + def bidderRequest = bidder.getBidderRequests(bidRequest.id) + assert bidderRequest.ext.prebid.floors.fetchStatus.every { it == null } + + and: "Alerts.general metrics shouldn't be populated" + def metrics = floorsPbsService.sendCollectedMetricsRequest() + assert !metrics[ALERT_GENERAL] } def "PBS should not invalidate previously good fetched data when floors provider return invalid data"() { diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy index 1de08b44cc9..d31c8223822 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy @@ -1,9 +1,5 @@ package org.prebid.server.functional.tests.pricefloors -import org.prebid.server.functional.model.config.AccountAuctionConfig -import org.prebid.server.functional.model.config.AccountConfig -import org.prebid.server.functional.model.config.AccountPriceFloorsConfig -import org.prebid.server.functional.model.db.Account import org.prebid.server.functional.model.db.StoredRequest import org.prebid.server.functional.model.pricefloors.Country import org.prebid.server.functional.model.pricefloors.ModelGroup @@ -33,23 +29,11 @@ import static org.prebid.server.functional.model.pricefloors.MediaType.VIDEO import static org.prebid.server.functional.model.pricefloors.PriceFloorField.MEDIA_TYPE import static org.prebid.server.functional.model.pricefloors.PriceFloorField.SITE_DOMAIN import static org.prebid.server.functional.model.request.auction.DistributionChannel.APP -import static org.prebid.server.functional.model.request.auction.FetchStatus.INPROGRESS -import static org.prebid.server.functional.model.request.auction.FetchStatus.NONE import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { - // Required: Sync no longer logs "in progress"—only "none" or "error" statuses are recorded - private static final String FETCHING_DISABLED_ERROR = 'Fetching is disabled' - private static final Closure INVALID_CONFIG_METRIC = { account -> "alerts.account_config.${account}.price-floors" } - private static final Closure WARNING_MESSAGE = { message -> - "$FETCHING_DISABLED_ERROR. Following parsing of request price floors is failed: $message" - } - private static final Closure ERROR_LOG = { bidRequest, message -> - "No price floor data for account ${bidRequest.accountId} and " + - "request ${bidRequest.id}, reason: ${WARNING_MESSAGE(message)}" - } private static final int MAX_SCHEMA_DIMENSIONS_SIZE = 1 private static final int MAX_RULES_SIZE = 1 private static Instant startTime @@ -58,7 +42,7 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { startTime = Instant.now() } - def "PBS should skip signalling for request with rules when ext.prebid.floors.enabled = false in request"() { + def "PBS should skip signaling for request with rules when ext.prebid.floors.enabled = false in request"() { given: "Default BidRequest with disabled floors" def bidRequest = bidRequestWithFloors.tap { ext.prebid.floors.enabled = requestEnabled @@ -70,24 +54,45 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { } accountDao.save(account) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + + and: "PBS fetch rules from floors provider" + cacheFloorsProviderRules(bidRequest, floorsPbsService, GENERIC, SUCCESS) + when: "PBS processes auction request" - floorsPbsService.sendAuctionRequest(bidRequest) + def response = floorsPbsService.sendAuctionRequest(bidRequest) then: "Bidder request bidFloor should correspond request" - def bidderRequest = bidder.getBidderRequest(bidRequest.id) + def bidderRequest = bidder.getBidderRequests(bidRequest.id).last assert bidderRequest.imp[0].bidFloor == bidRequest.imp[0].bidFloor assert !bidderRequest.ext?.prebid?.floors?.enabled and: "PBS should not fetch rules from floors provider" assert floorsProvider.getRequestCount(bidRequest.site.publisher.id) == 0 + and: "PBS should not add warning or errors" + assert !response.ext.warnings + assert !response.ext.errors + + and: "PBS shouldn't log a errors" + def message = 'Price floor rules data must be present' + def logs = floorsPbsService.getLogsByTime(startTime) + def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + assert !floorsLogs.size() + + and: "Alerts.general metrics shouldn't be populated" + def metrics = floorsPbsService.sendCollectedMetricsRequest() + assert !metrics[ALERT_GENERAL] + where: requestEnabled | accountEnabled false | true true | false } - def "PBS should skip signalling for request without rules when ext.prebid.floors.enabled = false in request"() { + def "PBS should skip signaling for request without rules when ext.prebid.floors.enabled = false in request"() { given: "Default BidRequest" def bidRequest = BidRequest.getDefaultBidRequest(APP).tap { ext.prebid.floors = new ExtPrebidFloors(enabled: false) @@ -166,7 +171,7 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { assert floorsProvider.getRequestCount(bidRequest.site.publisher.id) == 1 } - def "PBS should not signalling when neither fetched floors nor ext.prebid.floors exist, imp.bidFloor is not defined"() { + def "PBS should not signaling when neither fetched floors nor ext.prebid.floors exist, imp.bidFloor is not defined"() { given: "Default BidRequest" def bidRequest = BidRequest.defaultBidRequest @@ -194,7 +199,7 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { assert floorsProvider.getRequestCount(bidRequest.site.publisher.id) == 1 } - def "PBS should make PF signalling when skipRate = #skipRate"() { + def "PBS should make PF signaling when skipRate = #skipRate"() { given: "Default BidRequest with bidFloor, bidFloorCur" def bidRequest = BidRequest.defaultBidRequest.tap { imp[0].bidFloor = PBSUtils.randomFloorValue @@ -230,7 +235,7 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { skipRate << [0, null] } - def "PBS should not make PF signalling, enforcing when skipRate = 100"() { + def "PBS should not make PF signaling, enforcing when skipRate = 100"() { given: "Default BidRequest with bidFloor, bidFloorCur" def bidRequest = BidRequest.defaultBidRequest.tap { imp[0].bidFloor = 0.8 @@ -264,7 +269,7 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { assert bidderRequest.ext?.prebid?.floors?.skipRate == 100 assert bidderRequest.ext?.prebid?.floors?.skipped - and: "PBS should not made signalling" + and: "PBS should not made signaling" assert !bidderRequest.imp[0].ext?.prebid?.floors where: @@ -553,6 +558,10 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { def account = getAccountWithEnabledFetch(accountId) accountDao.save(account) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + when: "PBS processes auction request" def response = floorsPbsService.sendAuctionRequest(bidRequest) @@ -597,7 +606,7 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { then: "PBS should log a warning" def message = "Price floor rules number ${getRuleSize(bidRequest)} exceeded its maximum number ${MAX_RULES_SIZE}" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] and: "Alerts.general metrics should be populated" def metrics = floorsPbsService.sendCollectedMetricsRequest() @@ -628,13 +637,17 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { and: "Flush metrics" flushMetrics(floorsPbsService) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + when: "PBS processes auction request" def response = floorsPbsService.sendAuctionRequest(bidRequest) then: "PBS should log a warning" def message = "Price floor schema dimensions ${getSchemaSize(bidRequest)} exceeded its maximum number ${MAX_SCHEMA_DIMENSIONS_SIZE}" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] and: "Alerts.general metrics should be populated" def metrics = floorsPbsService.sendCollectedMetricsRequest() @@ -671,17 +684,21 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { and: "Flush metrics" flushMetrics(floorsPbsService) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + when: "PBS processes auction request" def response = floorsPbsService.sendAuctionRequest(bidRequest) then: "PBS should log a warning" def message = "Price floor schema dimensions ${getSchemaSize(bidRequest)} exceeded its maximum number ${MAX_SCHEMA_DIMENSIONS_SIZE}" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) assert floorsLogs.size() == 1 and: "Metrics should be updated" @@ -720,17 +737,21 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { } accountDao.save(account) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + when: "PBS processes auction request" def response = floorsPbsService.sendAuctionRequest(bidRequest) then: "Response should includer error warning" def message = "Price floor schema dimensions ${getSchemaSize(bidRequest)} exceeded its maximum number ${MAX_SCHEMA_DIMENSIONS_SIZE}" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] and: "PBS shouldn't log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) assert floorsLogs.size() == 1 and: "Metrics should be updated" @@ -757,17 +778,21 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { and: "Flush metrics" flushMetrics(floorsPbsService) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + when: "PBS processes auction request" def response = floorsPbsService.sendAuctionRequest(bidRequest) then: "PBS should log a warning" def message = "Price floor schema dimensions ${getSchemaSize(bidRequest)} exceeded its maximum number ${MAX_SCHEMA_DIMENSIONS_SIZE}" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" @@ -797,6 +822,10 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { and: "Flush metrics" flushMetrics(floorsPbsService) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + when: "PBS processes auction request" def response = floorsPbsService.sendAuctionRequest(bidRequest) @@ -804,11 +833,11 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { def message = "Price floor schema dimensions ${floorSchemaFilesSize} " + "exceeded its maximum number ${MAX_SCHEMA_DIMENSIONS_SIZE}" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" @@ -828,6 +857,10 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { } accountDao.save(account) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + when: "PBS processes auction request" def response = floorsPbsService.sendAuctionRequest(bidRequest) @@ -873,12 +906,12 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { def message = "Price floor rules number ${getRuleSize(ampStoredRequest)} " + "exceeded its maximum number ${MAX_RULES_SIZE}" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, "No price floor data for account $ampRequest.account and " + - "request $ampStoredRequest.id, reason: ${WARNING_MESSAGE(message)}") + "request $ampStoredRequest.id, reason: ${FETCHING_DISABLED_WARNING_MESSAGE(message)}") assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" @@ -891,84 +924,6 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { null | MAX_RULES_SIZE } - def "PBS shouldn't emit error in log and response when data is invalid and floors status is in progress"() { - given: "Default BidRequest with empty floors.data" - def bidRequest = bidRequestWithFloors.tap { - ext.prebid.floors.data = null - } - - and: "Account with enabled fetching" - def account = getAccountWithEnabledFetch(bidRequest.accountId) - accountDao.save(account) - - and: "Flush metrics" - flushMetrics(floorsPbsService) - - when: "PBS processes auction request" - def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) - - then: "Response shouldn't includer error warning" - assert !bidResponse.ext?.warnings - - and: "PBS shouldn't log a errors" - def message = 'Price floor rules data must be present' - def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, ERROR_LOG(bidRequest, message)) - assert !floorsLogs.size() - - and: "PBS request on response object status should be in progress" - assert getRequests(bidResponse)[GENERIC.value]?.first?.ext?.prebid?.floors?.fetchStatus == INPROGRESS - - and: "PBS bidderRequest status should be in progress" - def bidderRequest = bidder.getBidderRequests(bidRequest.id) - assert bidderRequest.ext.prebid.floors.fetchStatus == [INPROGRESS] - - and: "Alerts.general metrics shouldn't be populated" - def metrics = floorsPbsService.sendCollectedMetricsRequest() - assert !metrics[ALERT_GENERAL] - } - - def "PBS should emit error in log and response when data is invalid and floors status not in progress"() { - given: "Default BidRequest with empty floors.data" - def bidRequest = bidRequestWithFloors.tap { - ext.prebid.floors.data = null - } - - and: "Flush metrics" - flushMetrics(floorsPbsService) - - and: "Account with disabled fetching" - def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { - config.auction.priceFloors.fetch.enabled = false - } - accountDao.save(account) - - when: "PBS processes auction request" - def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) - - then: "PBS should log a warning" - def message = 'Price floor rules data must be present' - assert bidResponse.ext?.warnings[PREBID]*.code == [999] - assert bidResponse.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] - - and: "PBS should log a errors" - def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, ERROR_LOG(bidRequest, message)) - assert floorsLogs.size() == 1 - - and: "PBS request on response object status should be none" - assert getRequests(bidResponse)[GENERIC.value]?.first?.ext?.prebid?.floors?.fetchStatus == NONE - - and: "PBS request status shouldn't be in progress" - def bidderRequest = bidder.getBidderRequests(bidRequest.id) - assert bidderRequest.ext.prebid.floors.fetchStatus == [NONE] - - - and: "Alerts.general metrics should be populated" - def metrics = floorsPbsService.sendCollectedMetricsRequest() - assert metrics[ALERT_GENERAL] == 1 - } - def "PBS should emit error in log and response when floors skipRate is out of range"() { given: "BidRequest with invalid skipRate" def bidRequest = bidRequestWithFloors.tap { @@ -981,14 +936,18 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { when: "PBS processes auction request" def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) + and: "Default bid response" + def response = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, response) + then: "PBS should log a warning" def message = "Price floor data skipRate must be in range(0-100), but was $requestSkipRate" assert bidResponse.ext?.warnings[PREBID]*.code == [999] - assert bidResponse.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert bidResponse.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" @@ -1008,17 +967,21 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { and: "Flush metrics" flushMetrics(floorsPbsService) + and: "Default bid response" + def response = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, response) + when: "PBS processes auction request" def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) then: "PBS should log a warning" def message = "Price floor rules should contain at least one model group" assert bidResponse.ext?.warnings[PREBID]*.code == [999] - assert bidResponse.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert bidResponse.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" @@ -1040,17 +1003,21 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { and: "Flush metrics" flushMetrics(floorsPbsService) + and: "Default bid response" + def response = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, response) + when: "PBS processes auction request" def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) then: "PBS should log a warning" def message = "Price floor modelGroup modelWeight must be in range(1-100), but was $requestModelWeight" assert bidResponse.ext?.warnings[PREBID]*.code == [999] - assert bidResponse.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert bidResponse.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" @@ -1073,17 +1040,21 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { and: "Flush metrics" flushMetrics(floorsPbsService) + and: "Default bid response" + def response = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, response) + when: "PBS processes auction request" def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) then: "PBS should log a warning" def message = "Price floor modelGroup skipRate must be in range(0-100), but was $requestModelGroupsSkipRate" assert bidResponse.ext?.warnings[PREBID]*.code == [999] - assert bidResponse.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert bidResponse.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] and: "PBS should log an errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" @@ -1104,17 +1075,21 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { and: "Flush metrics" flushMetrics(floorsPbsService) + and: "Default bid response" + def response = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, response) + when: "PBS processes auction request" def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) then: "PBS should log a warning" def message = "Price floor modelGroup default must be positive float, but was $requestModelGroupsSkipRate" assert bidResponse.ext?.warnings[PREBID]*.code == [999] - assert bidResponse.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] + assert bidResponse.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" @@ -1122,33 +1097,6 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { assert metrics[ALERT_GENERAL] == 1 } - def "PBS should use default floors config when original account config is invalid"() { - given: "Bid request with empty floors" - def bidRequest = BidRequest.defaultBidRequest - - and: "Account with disabled price floors" - def accountConfig = new AccountConfig(auction: new AccountAuctionConfig(priceFloors: new AccountPriceFloorsConfig(enabled: false))) - def account = new Account(uuid: bidRequest.accountId, config: accountConfig) - accountDao.save(account) - - and: "Flush metrics" - flushMetrics(floorsPbsService) - - when: "PBS processes auction request" - def response = floorsPbsService.sendAuctionRequest(bidRequest) - - and: "PBS floors validation failure should not reject the entire auction" - assert !response.seatbid?.isEmpty() - - then: "PBS shouldn't log warning or errors" - assert !response.ext?.warnings - assert !response.ext?.errors - - and: "Alerts.general metrics shouldn't be populated" - def metrics = floorsPbsService.sendCollectedMetricsRequest() - assert !metrics[ALERT_GENERAL] - } - def "PBS should emit error in log and response when account have disabled dynamic data config"() { given: "Bid request without floors" def bidRequest = BidRequest.defaultBidRequest @@ -1162,6 +1110,10 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { and: "PBS fetch rules from floors provider" cacheFloorsProviderRules(bidRequest, floorsPbsService, GENERIC, SUCCESS) + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + and: "Flush metrics" flushMetrics(floorsPbsService) @@ -1183,6 +1135,14 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { assert metrics[ALERT_GENERAL] == 1 } + def "PBS shouldn't emit error or warning when floors for account configured correctly"() { + given: "Bid request without floors" + def bidRequest = BidRequest.defaultBidRequest + + and: "PBS fetch rules from floors provider" + cacheFloorsProviderRules(bidRequest, floorsPbsService, GENERIC, SUCCESS) + } + private static int getSchemaSize(BidRequest bidRequest) { bidRequest?.ext?.prebid?.floors?.data?.modelGroups[0].schema.fields.size() } diff --git a/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy b/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy index e1e7750ea05..1243303fa4f 100644 --- a/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy +++ b/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy @@ -111,6 +111,10 @@ class PBSUtils implements ObjectMapperWrapper { getRandomDecimal(min, max).setScale(scale, HALF_UP) } + static BigDecimal getRandomPrice(BigDecimal min, BigDecimal max = 10, int scale = 3) { + getRandomDecimal(min, max).setScale(scale, HALF_UP) + } + static > T getRandomEnum(Class anEnum, List exclude = []) { def values = anEnum.enumConstants.findAll { !exclude.contains(it) } as T[] values[getRandomNumber(0, values.size() - 1)] From 44426fa55fa1756e0760fff1a142fcf4b74e932a Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Mon, 5 May 2025 11:44:02 +0300 Subject: [PATCH 3/9] Tests: Increase coverage for floors logs tests --- .../PriceFloorsFetchingSpec.groovy | 46 +++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy index 767445c04ce..4503485ac0d 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy @@ -1628,7 +1628,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def "PBS shouldn't emit error in log and response when floors is not in request and floors fetching disabled for account"() { given: "Account with disabled fetching" - def priceFloors = new AccountPriceFloorsConfig(enabled: false, fetch: new PriceFloorsFetch(enabled: false)) + def priceFloors = new AccountPriceFloorsConfig(enabled: true, fetch: new PriceFloorsFetch(enabled: false)) def accountAuctionConfig = new AccountAuctionConfig(priceFloors: priceFloors) def accountConfig = new AccountConfig(auction: accountAuctionConfig) def account = new Account(uuid: bidRequest.accountId, config: accountConfig) @@ -1669,10 +1669,10 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { bidRequest << [BidRequest.getDefaultBidRequest(), getBidRequestWithFloors().tap { it.ext.prebid.floors = null }] } - def "PBS should emit error in log and response when data is invalid and floors fetching disabled for account and #requestFloors for request"() { + def "PBS should emit error in log and response when data is invalid and floors fetching disabled for account and enabled for request"() { given: "Default BidRequest with empty floors.data" def bidRequest = bidRequestWithFloors.tap { - ext.prebid.floors.enabled = requestFloors + ext.prebid.floors.enabled = true ext.prebid.floors.data = null } @@ -1715,9 +1715,47 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Alerts.general metrics should be populated" def metrics = floorsPbsService.sendCollectedMetricsRequest() assert metrics[ALERT_GENERAL] == 1 + } + + def "PBS shouldn't emit error in log and response when data is invalid and floors fetching disabled for account and #requestFloors for request"() { + given: "Default BidRequest with empty floors.data" + def bidRequest = bidRequestWithFloors.tap { + ext.prebid.floors.enabled = requestFloors + ext.prebid.floors.data = null + } + + and: "Flush metrics" + flushMetrics(floorsPbsService) + + and: "Account with disabled fetching" + def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { + config.auction.priceFloors.fetch.enabled = false + } + accountDao.save(account) + + and: "Default bid response" + def response = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, response) + + when: "PBS processes auction request" + def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) + + then: "PBS shouldn't log warning or errors" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS shouldn't log a errors" + def message = 'Price floor rules data must be present' + def logs = floorsPbsService.getLogsByTime(startTime) + def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + assert !floorsLogs.size() + + and: "Alerts.general metrics shouldn't be populated" + def metrics = floorsPbsService.sendCollectedMetricsRequest() + assert !metrics[ALERT_GENERAL] where: - requestFloors << [null, true] + requestFloors << [null, false] } def "PBS shouldn't emit error in log and response when data is invalid and floors fetching enabled for account"() { From 98d9edb7dbb8f2d2790a073bf866b3f1ef4de7c7 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Mon, 5 May 2025 15:38:33 +0300 Subject: [PATCH 4/9] update after review --- .../model/response/auction/Bid.groovy | 2 +- .../pricefloors/PriceFloorsBaseSpec.groovy | 3 +- .../PriceFloorsFetchingSpec.groovy | 48 +++++++++---------- .../PriceFloorsSignalingSpec.groovy | 8 ---- .../server/functional/util/PBSUtils.groovy | 4 -- 5 files changed, 24 insertions(+), 41 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/Bid.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/Bid.groovy index be764c64df4..127ed32bfd9 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/Bid.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/Bid.groovy @@ -60,7 +60,7 @@ class Bid implements ObjectMapperWrapper { new Bid().tap { id = UUID.randomUUID() impid = imp.id - price = imp.bidFloor != null ? PBSUtils.getRandomPrice(imp.bidFloor) : PBSUtils.getRandomPrice() + price = imp.bidFloor != null ? imp.bidFloor : PBSUtils.getRandomPrice() crid = 1 height = imp.banner && imp.banner.format ? imp.banner.format.first().height : null weight = imp.banner && imp.banner.format ? imp.banner.format.first().weight : null diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsBaseSpec.groovy index a8b18661abf..77b1a0f9325 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsBaseSpec.groovy @@ -57,8 +57,7 @@ abstract class PriceFloorsBaseSpec extends BaseSpec { "No price floor data for account ${bidRequest.accountId} and " + "request ${bidRequest.id}, reason: ${URL_EMPTY_WARNING_MESSAGE("$BASIC_FETCH_URL$bidRequest.accountId", message)}" } - // Required: Sync no longer logs "in progress"—only "none" or "error" statuses are recorded - protected static final String FETCHING_DISABLED_ERROR = 'Fetching is disabled' + protected static final String FETCHING_DISABLED_ERROR = "Fetching is disabled" protected static final Closure FETCHING_DISABLED_WARNING_MESSAGE = { message -> "$FETCHING_DISABLED_ERROR. Following parsing of request price floors is failed: $message" } diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy index 4503485ac0d..0b379c8b795 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy @@ -1,10 +1,6 @@ package org.prebid.server.functional.tests.pricefloors -import org.prebid.server.functional.model.config.AccountAuctionConfig -import org.prebid.server.functional.model.config.AccountConfig -import org.prebid.server.functional.model.config.AccountPriceFloorsConfig import org.prebid.server.functional.model.config.PriceFloorsFetch -import org.prebid.server.functional.model.db.Account import org.prebid.server.functional.model.db.StoredRequest import org.prebid.server.functional.model.pricefloors.ModelGroup import org.prebid.server.functional.model.pricefloors.PriceFloorData @@ -1627,20 +1623,20 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { } def "PBS shouldn't emit error in log and response when floors is not in request and floors fetching disabled for account"() { - given: "Account with disabled fetching" - def priceFloors = new AccountPriceFloorsConfig(enabled: true, fetch: new PriceFloorsFetch(enabled: false)) - def accountAuctionConfig = new AccountAuctionConfig(priceFloors: priceFloors) - def accountConfig = new AccountConfig(auction: accountAuctionConfig) - def account = new Account(uuid: bidRequest.accountId, config: accountConfig) + given: "Account with disabled fetching" + def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { + config.auction.priceFloors.fetch.enabled = false + config.auction.priceFloors.fetch.url = null + } accountDao.save(account) - and: "Flush metrics" - flushMetrics(floorsPbsService) - and: "Default bid response" def response = BidResponse.getDefaultBidResponse(bidRequest) bidder.setResponse(bidRequest.id, response) + and: "Flush metrics" + flushMetrics(floorsPbsService) + when: "PBS processes auction request" def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) @@ -1669,16 +1665,13 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { bidRequest << [BidRequest.getDefaultBidRequest(), getBidRequestWithFloors().tap { it.ext.prebid.floors = null }] } - def "PBS should emit error in log and response when data is invalid and floors fetching disabled for account and enabled for request"() { + def "PBS should emit error in log and response when floor data is empty and floors fetching disabled for account and enabled for request"() { given: "Default BidRequest with empty floors.data" def bidRequest = bidRequestWithFloors.tap { ext.prebid.floors.enabled = true ext.prebid.floors.data = null } - and: "Flush metrics" - flushMetrics(floorsPbsService) - and: "Account with disabled fetching" def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { config.auction.priceFloors.fetch.enabled = false @@ -1689,6 +1682,9 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def response = BidResponse.getDefaultBidResponse(bidRequest) bidder.setResponse(bidRequest.id, response) + and: "Flush metrics" + flushMetrics(floorsPbsService) + when: "PBS processes auction request" def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) @@ -1717,16 +1713,13 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { assert metrics[ALERT_GENERAL] == 1 } - def "PBS shouldn't emit error in log and response when data is invalid and floors fetching disabled for account and #requestFloors for request"() { + def "PBS shouldn't emit error in log and response when floor data is empty and floors fetching disabled for account and #requestFloors for request"() { given: "Default BidRequest with empty floors.data" def bidRequest = bidRequestWithFloors.tap { ext.prebid.floors.enabled = requestFloors ext.prebid.floors.data = null } - and: "Flush metrics" - flushMetrics(floorsPbsService) - and: "Account with disabled fetching" def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { config.auction.priceFloors.fetch.enabled = false @@ -1737,6 +1730,9 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def response = BidResponse.getDefaultBidResponse(bidRequest) bidder.setResponse(bidRequest.id, response) + and: "Flush metrics" + flushMetrics(floorsPbsService) + when: "PBS processes auction request" def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) @@ -1772,13 +1768,13 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { } accountDao.save(account) - and: "Flush metrics" - flushMetrics(floorsPbsService) - and: "Default bid response" def response = BidResponse.getDefaultBidResponse(bidRequest) bidder.setResponse(bidRequest.id, response) + and: "Flush metrics" + flushMetrics(floorsPbsService) + when: "PBS processes auction request" def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) @@ -1814,9 +1810,6 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { ext.prebid.floors.data = null } - and: "Flush metrics" - flushMetrics(floorsPbsService) - and: "Account with disabled fetching" def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { config.auction.priceFloors.enabled = false @@ -1831,6 +1824,9 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def response = BidResponse.getDefaultBidResponse(bidRequest) bidder.setResponse(bidRequest.id, response) + and: "Flush metrics" + flushMetrics(floorsPbsService) + when: "PBS processes auction request" def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy index d31c8223822..335502bd7fc 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy @@ -1135,14 +1135,6 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { assert metrics[ALERT_GENERAL] == 1 } - def "PBS shouldn't emit error or warning when floors for account configured correctly"() { - given: "Bid request without floors" - def bidRequest = BidRequest.defaultBidRequest - - and: "PBS fetch rules from floors provider" - cacheFloorsProviderRules(bidRequest, floorsPbsService, GENERIC, SUCCESS) - } - private static int getSchemaSize(BidRequest bidRequest) { bidRequest?.ext?.prebid?.floors?.data?.modelGroups[0].schema.fields.size() } diff --git a/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy b/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy index 1243303fa4f..e1e7750ea05 100644 --- a/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy +++ b/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy @@ -111,10 +111,6 @@ class PBSUtils implements ObjectMapperWrapper { getRandomDecimal(min, max).setScale(scale, HALF_UP) } - static BigDecimal getRandomPrice(BigDecimal min, BigDecimal max = 10, int scale = 3) { - getRandomDecimal(min, max).setScale(scale, HALF_UP) - } - static > T getRandomEnum(Class anEnum, List exclude = []) { def values = anEnum.enumConstants.findAll { !exclude.contains(it) } as T[] values[getRandomNumber(0, values.size() - 1)] From c1a3ac1c4470e193ebd25d1893d92d726b2ce927 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Wed, 7 May 2025 12:39:54 +0300 Subject: [PATCH 5/9] update tests --- pom.xml | 2 +- .../pricefloors/PriceFloorsBaseSpec.groovy | 36 +++++++++++-------- .../PriceFloorsFetchingSpec.groovy | 6 +++- .../PriceFloorsSignalingSpec.groovy | 14 ++++++-- 4 files changed, 39 insertions(+), 19 deletions(-) diff --git a/pom.xml b/pom.xml index 2617ea9fcc1..7be92568153 100644 --- a/pom.xml +++ b/pom.xml @@ -367,7 +367,7 @@ ${maven-surefire-plugin.version} - false + true ${skipUnitTests} diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsBaseSpec.groovy index 77b1a0f9325..7fad52cb165 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsBaseSpec.groovy @@ -37,35 +37,41 @@ abstract class PriceFloorsBaseSpec extends BaseSpec { public static final BigDecimal FLOOR_MAX = 2 public static final Map FLOORS_CONFIG = ["price-floors.enabled": "true"] - protected static final String BASIC_FETCH_URL = networkServiceContainer.rootUri + FloorsProvider.FLOORS_ENDPOINT - protected static final int MAX_MODEL_WEIGHT = 100 - - private static final int DEFAULT_MODEL_WEIGHT = 1 - private static final int CURRENCY_CONVERSION_PRECISION = 3 - private static final int FLOOR_VALUE_PRECISION = 4 - protected static final FloorsProvider floorsProvider = new FloorsProvider(networkServiceContainer) - protected final PrebidServerService floorsPbsService = pbsServiceFactory.getService(FLOORS_CONFIG + GENERIC_ALIAS_CONFIG) + protected static final String BASIC_FETCH_URL = networkServiceContainer.rootUri + FloorsProvider.FLOORS_ENDPOINT + protected static final int MAX_MODEL_WEIGHT = 100 protected static final Closure INVALID_CONFIG_METRIC = { account -> "alerts.account_config.${account}.price-floors" } - protected static final Closure URL_EMPTY_ERROR = { url -> "Failed to fetch price floor from provider for fetch.url '${url}'" } + protected static final Closure URL_EMPTY_ERROR = { url -> + "Failed to fetch price floor from provider for fetch.url '${url}'" + } protected static final Closure URL_EMPTY_WARNING_MESSAGE = { url, message -> - "${URL_EMPTY_ERROR(url)}, with a reason: $message" + PRICE_FLOORS_WARNING_MESSAGE(URL_EMPTY_ERROR(url), "Reason: $message") } protected static final Closure URL_EMPTY_ERROR_LOG = { bidRequest, message -> - "No price floor data for account ${bidRequest.accountId} and " + - "request ${bidRequest.id}, reason: ${URL_EMPTY_WARNING_MESSAGE("$BASIC_FETCH_URL$bidRequest.accountId", message)}" + PRICE_FLOORS_ERROR_LOG(bidRequest, URL_EMPTY_WARNING_MESSAGE("$BASIC_FETCH_URL${bidRequest.accountId}", message)) } protected static final String FETCHING_DISABLED_ERROR = "Fetching is disabled" protected static final Closure FETCHING_DISABLED_WARNING_MESSAGE = { message -> - "$FETCHING_DISABLED_ERROR. Following parsing of request price floors is failed: $message" + PRICE_FLOORS_WARNING_MESSAGE(FETCHING_DISABLED_ERROR, "Following parsing of request price floors is failed: $message") } protected static final Closure FETCHING_DISABLED_ERROR_LOG = { bidRequest, message -> - "No price floor data for account ${bidRequest.accountId} and " + - "request ${bidRequest.id}, reason: ${FETCHING_DISABLED_WARNING_MESSAGE(message)}" + PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_WARNING_MESSAGE(message)) + } + private static final Closure PRICE_FLOORS_WARNING_MESSAGE = { reason, details -> + "Price floors processing failed: $reason. $details" + } + private static final Closure PRICE_FLOORS_ERROR_LOG = { bidRequest, warningMessage -> + "Price Floors can't be resolved for account ${bidRequest.accountId} and request ${bidRequest.id}, reason: $warningMessage" } + private static final int DEFAULT_MODEL_WEIGHT = 1 + private static final int CURRENCY_CONVERSION_PRECISION = 3 + private static final int FLOOR_VALUE_PRECISION = 4 + + protected final PrebidServerService floorsPbsService = pbsServiceFactory.getService(FLOORS_CONFIG + GENERIC_ALIAS_CONFIG) + def setupSpec() { floorsProvider.setResponse() } diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy index 0b379c8b795..3c0cdf2e175 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy @@ -11,6 +11,7 @@ import org.prebid.server.functional.model.request.auction.ExtPrebidFloors import org.prebid.server.functional.model.request.auction.PrebidStoredRequest import org.prebid.server.functional.model.response.auction.BidResponse import org.prebid.server.functional.util.PBSUtils +import spock.lang.IgnoreRest import java.time.Instant @@ -530,12 +531,15 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { } } + @IgnoreRest def "PBS should log error and increase metrics when useFetchDataRate have invalid value"() { given: "Test start time" def startTime = Instant.now() and: "Default BidRequest" - def bidRequest = BidRequest.defaultBidRequest + def bidRequest = getBidRequestWithFloors().tap { + ext.prebid.floors.data.useFetchDataRate = -1 + } and: "Account with enabled fetch and fetch.url in the DB" def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy index 335502bd7fc..393a8f8cec0 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy @@ -17,6 +17,7 @@ import org.prebid.server.functional.model.request.auction.Video import org.prebid.server.functional.model.response.auction.BidResponse import org.prebid.server.functional.model.response.auction.MediaType import org.prebid.server.functional.util.PBSUtils +import spock.lang.IgnoreRest import java.time.Instant @@ -910,7 +911,7 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, "No price floor data for account $ampRequest.account and " + + def floorsLogs = getLogsByText(logs, "Price Floors can't be resolved for account $ampRequest.account and " + "request $ampStoredRequest.id, reason: ${FETCHING_DISABLED_WARNING_MESSAGE(message)}") assert floorsLogs.size() == 1 @@ -1097,9 +1098,18 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { assert metrics[ALERT_GENERAL] == 1 } +// @IgnoreRest def "PBS should emit error in log and response when account have disabled dynamic data config"() { given: "Bid request without floors" - def bidRequest = BidRequest.defaultBidRequest + def bidRequest = getBidRequestWithFloors().tap { + ext.prebid.floors.data = null + } + + def floorValue = PBSUtils.randomFloorValue + def floorsResponse = PriceFloorData.priceFloorData.tap { + modelGroups[0].values = [(rule): floorValue] + useFetchDataRate = -100 + } and: "Account with disabled dynamic data" def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { From d38149bd98d03554044ab3a84e86f35d3b45e82e Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Wed, 7 May 2025 23:02:53 +0300 Subject: [PATCH 6/9] update tests --- pom.xml | 2 +- .../pricefloors/PriceFloorsBaseSpec.groovy | 25 +-- .../PriceFloorsFetchingSpec.groovy | 160 +++++++++++++----- .../PriceFloorsSignalingSpec.groovy | 61 +++---- 4 files changed, 158 insertions(+), 90 deletions(-) diff --git a/pom.xml b/pom.xml index 7be92568153..2617ea9fcc1 100644 --- a/pom.xml +++ b/pom.xml @@ -367,7 +367,7 @@ ${maven-surefire-plugin.version} - true + false ${skipUnitTests} diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsBaseSpec.groovy index 7fad52cb165..a604264b264 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsBaseSpec.groovy @@ -43,27 +43,20 @@ abstract class PriceFloorsBaseSpec extends BaseSpec { protected static final int MAX_MODEL_WEIGHT = 100 protected static final Closure INVALID_CONFIG_METRIC = { account -> "alerts.account_config.${account}.price-floors" } - protected static final Closure URL_EMPTY_ERROR = { url -> - "Failed to fetch price floor from provider for fetch.url '${url}'" - } - protected static final Closure URL_EMPTY_WARNING_MESSAGE = { url, message -> - PRICE_FLOORS_WARNING_MESSAGE(URL_EMPTY_ERROR(url), "Reason: $message") - } - protected static final Closure URL_EMPTY_ERROR_LOG = { bidRequest, message -> - PRICE_FLOORS_ERROR_LOG(bidRequest, URL_EMPTY_WARNING_MESSAGE("$BASIC_FETCH_URL${bidRequest.accountId}", message)) + protected static final Closure URL_EMPTY_ERROR = { url -> "Failed to fetch price floor from provider for fetch.url '${url}'" } protected static final String FETCHING_DISABLED_ERROR = "Fetching is disabled" - protected static final Closure FETCHING_DISABLED_WARNING_MESSAGE = { message -> - PRICE_FLOORS_WARNING_MESSAGE(FETCHING_DISABLED_ERROR, "Following parsing of request price floors is failed: $message") + protected static final Closure PRICE_FLOORS_ERROR_LOG = { bidRequest, reason, warningMessage -> + "Price Floors can't be resolved for account ${bidRequest.accountId} and request ${bidRequest.id}, reason: ${PRICE_FLOORS_WARNING_MESSAGE(reason, warningMessage)}" } - protected static final Closure FETCHING_DISABLED_ERROR_LOG = { bidRequest, message -> - PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_WARNING_MESSAGE(message)) + protected static final Closure WARNING_MESSAGE = { message -> + "Price floors processing failed: parsing of request price floors is failed: $message" } - private static final Closure PRICE_FLOORS_WARNING_MESSAGE = { reason, details -> - "Price floors processing failed: $reason. $details" + protected static final Closure FETCHING_FLOORS_ERROR_LOG = { bidRequest, warningMessage -> + "Price floor fetching failed for account ${bidRequest.accountId}: ${URL_EMPTY_ERROR("$BASIC_FETCH_URL${bidRequest.accountId}")}, with a reason: $warningMessage" } - private static final Closure PRICE_FLOORS_ERROR_LOG = { bidRequest, warningMessage -> - "Price Floors can't be resolved for account ${bidRequest.accountId} and request ${bidRequest.id}, reason: $warningMessage" + private static final Closure PRICE_FLOORS_WARNING_MESSAGE = { reason, details -> + "Price floors processing failed: $reason. Following parsing of request price floors is failed: $details" } private static final int DEFAULT_MODEL_WEIGHT = 1 diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy index 3c0cdf2e175..57e21a4e3af 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy @@ -11,7 +11,6 @@ import org.prebid.server.functional.model.request.auction.ExtPrebidFloors import org.prebid.server.functional.model.request.auction.PrebidStoredRequest import org.prebid.server.functional.model.response.auction.BidResponse import org.prebid.server.functional.util.PBSUtils -import spock.lang.IgnoreRest import java.time.Instant @@ -131,8 +130,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, bidRequest.accountId) assert floorsLogs.size() == 1 - assert floorsLogs.first().contains("No price floor data for account $bidRequest.accountId and request $bidRequest.id, " + - "reason: Malformed fetch.url 'null' passed") + assert floorsLogs.first().contains("alformed fetch.url: 'null' passed for account $bidRequest.accountId") and: "PBS floors validation failure should not reject the entire auction" assert !response.seatbid.isEmpty() @@ -531,15 +529,12 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { } } - @IgnoreRest def "PBS should log error and increase metrics when useFetchDataRate have invalid value"() { given: "Test start time" def startTime = Instant.now() and: "Default BidRequest" - def bidRequest = getBidRequestWithFloors().tap { - ext.prebid.floors.data.useFetchDataRate = -1 - } + def bidRequest = BidRequest.getDefaultBidRequest() and: "Account with enabled fetch and fetch.url in the DB" def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { @@ -571,12 +566,16 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "PBS should fetch data" assert floorsProvider.getRequestCount(bidRequest.accountId) == 1 + and: "PBS should not add warning or errors" + assert !response.ext.warnings + assert !response.ext.errors + and: "PBS log should contain error" def logs = floorsPbsService.getLogsByTime(startTime) def message = "Price floor data useFetchDataRate must be in range(0-100), but was $accounntUseFetchDataRate" def floorsLogs = getLogsByText(logs, "$BASIC_FETCH_URL$bidRequest.accountId") assert floorsLogs.size() == 1 - assert floorsLogs[0].contains(URL_EMPTY_ERROR_LOG(bidRequest, message)) + assert floorsLogs[0].contains(FETCHING_FLOORS_ERROR_LOG(bidRequest, message)) and: "Floors validation failure cannot reject the entire auction" assert !response.seatbid?.isEmpty() @@ -604,6 +603,90 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { ] } + def "PBS should log merged error and increase metrics when useFetchDataRate have invalid value from provider and request"() { + given: "BidRequest with invalid floors" + def requestUseFetchDataRate = PBSUtils.getRandomNegativeNumber() + def bidRequest = getBidRequestWithFloors().tap { + it.ext.prebid.floors.data.useFetchDataRate = requestUseFetchDataRate + } + + and: "Account with enabled fetch and fetch.url in the DB" + def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { + config.auction.priceFloors.useDynamicData = true + } + accountDao.save(account) + + and: "Flush metrics" + flushMetrics(floorsPbsService) + + and: "Set Floors Provider response" + def providerUseFetchDataRate = PBSUtils.getRandomNegativeNumber() + def floorValue = PBSUtils.randomFloorValue + def floorsResponse = PriceFloorData.priceFloorData.tap { + modelGroups[0].values = [(rule): floorValue] + useFetchDataRate = providerUseFetchDataRate + } + floorsProvider.setResponse(bidRequest.accountId, floorsResponse) + + and: "Default bid response" + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidResponse) + + and: "PBS fetch rules from floors provider" + cacheFloorsProviderRules(bidRequest, floorsPbsService, GENERIC, ERROR) + + and: "Test start time" + def startTime = Instant.now() + + when: "PBS processes auction request" + def response = floorsPbsService.sendAuctionRequest(bidRequest) + + then: "metric should be updated" + def metrics = floorsPbsService.sendCollectedMetricsRequest() + assert metrics[FETCH_FAILURE_METRIC] == 1 + + and: "PBS should add single warning" + assert response.ext?.warnings[PREBID]*.code == [999] + assert response.ext?.warnings[PREBID]*.message == + [WARNING_MESSAGE("Price floor data useFetchDataRate must be in range(0-100), but was $requestUseFetchDataRate")] + + and: "PBS should not add error to request" + assert !response.ext.errors + + and: "PBS should fetch data" + assert floorsProvider.getRequestCount(bidRequest.accountId) == 1 + + and: "PBS log should contain combined error log" + def logs = floorsPbsService.getLogsByTime(startTime) + def fetchingErrorLogs = getLogsByText(logs, "Price Floors can't be resolved for account $bidRequest.accountId " + + "and request $bidRequest.id, reason: Price floors processing failed: Failed to fetch price floor from provider " + + "for fetch.url '$BASIC_FETCH_URL${bidRequest.accountId}', with a reason: " + + "Price floor data useFetchDataRate must be in range(0-100), but was $providerUseFetchDataRate. " + + "Following parsing of request price floors is failed: " + + "Price floor data useFetchDataRate must be in range(0-100), but was $requestUseFetchDataRate") + assert fetchingErrorLogs.size() == 1 + + and: "Floors validation failure cannot reject the entire auction" + assert !response.seatbid?.isEmpty() + + and: "Bidder request should contain floors data from floors provider" + def bidderRequest = bidder.getBidderRequests(bidRequest.id).last + verifyAll(bidderRequest) { + imp.bidFloor == bidRequest.imp.bidFloor + imp.bidFloorCur == bidRequest.imp.bidFloorCur + + !imp[0].ext?.prebid?.floors?.floorRule + !imp[0].ext?.prebid?.floors?.floorRuleValue + !imp[0].ext?.prebid?.floors?.floorValue + + !ext?.prebid?.floors?.floorProvider + !ext?.prebid?.floors?.skipRate + !ext?.prebid?.floors?.data + ext?.prebid?.floors?.location == NO_DATA + ext?.prebid?.floors?.fetchStatus == ERROR + } + } + def "PBS should process floors from request when use-dynamic-data = false"() { given: "Pbs with PF configuration with useDynamicData" def defaultAccountConfigSettings = defaultAccountConfigSettings.tap { @@ -676,7 +759,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, "$BASIC_FETCH_URL$bidRequest.accountId") assert floorsLogs.size() == 1 - assert floorsLogs[0].contains(URL_EMPTY_ERROR_LOG(bidRequest, message)) + assert floorsLogs[0].contains(FETCHING_FLOORS_ERROR_LOG(bidRequest, message)) and: "Floors validation failure cannot reject the entire auction" assert !response.seatbid?.isEmpty() @@ -718,7 +801,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, "$BASIC_FETCH_URL$bidRequest.accountId") assert floorsLogs.size() == 1 - assert floorsLogs[0].contains(URL_EMPTY_ERROR_LOG(bidRequest, message)) + assert floorsLogs[0].contains(FETCHING_FLOORS_ERROR_LOG(bidRequest, message)) and: "Floors validation failure cannot reject the entire auction" assert !response.seatbid?.isEmpty() @@ -759,7 +842,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, "$BASIC_FETCH_URL$bidRequest.accountId") assert floorsLogs.size() == 1 - assert floorsLogs[0].contains(URL_EMPTY_ERROR_LOG(bidRequest, message)) + assert floorsLogs[0].contains(FETCHING_FLOORS_ERROR_LOG(bidRequest, message)) and: "Floors validation failure cannot reject the entire auction" assert !response.seatbid?.isEmpty() @@ -803,7 +886,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, "$BASIC_FETCH_URL$bidRequest.accountId") assert floorsLogs.size() == 1 - assert floorsLogs[0].contains(URL_EMPTY_ERROR_LOG(bidRequest, message)) + assert floorsLogs[0].contains(FETCHING_FLOORS_ERROR_LOG(bidRequest, message)) and: "Floors validation failure cannot reject the entire auction" assert !response.seatbid?.isEmpty() @@ -846,7 +929,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, "$BASIC_FETCH_URL$bidRequest.accountId") assert floorsLogs.size() == 1 - assert floorsLogs[0].contains(URL_EMPTY_ERROR_LOG(bidRequest, PRICE_FLOOR_VALUES_MISSING)) + assert floorsLogs[0].contains(FETCHING_FLOORS_ERROR_LOG(bidRequest, PRICE_FLOOR_VALUES_MISSING)) and: "Floors validation failure cannot reject the entire auction" assert !response.seatbid?.isEmpty() @@ -893,7 +976,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, "$BASIC_FETCH_URL$bidRequest.accountId") assert floorsLogs.size() == 1 - assert floorsLogs[0].contains(URL_EMPTY_ERROR_LOG(bidRequest, message)) + assert floorsLogs[0].contains(FETCHING_FLOORS_ERROR_LOG(bidRequest, message)) and: "Floors validation failure cannot reject the entire auction" assert !response.seatbid?.isEmpty() @@ -938,10 +1021,9 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "PBS log should contain error" def logs = pbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, BASIC_FETCH_URL) + def floorsLogs = getLogsByText(logs, "Price floor fetching failed for account $bidRequest.accountId: " + + "Fetch price floor request timeout for fetch.url '$BASIC_FETCH_URL$accountId' exceeded") assert floorsLogs.size() == 1 - assert floorsLogs[0].contains("No price floor data for account $bidRequest.accountId and request $bidRequest.id, " + - "reason: Fetch price floor request timeout for fetch.url '$BASIC_FETCH_URL$accountId' exceeded") and: "Floors validation failure cannot reject the entire auction" assert !response.seatbid?.isEmpty() @@ -987,7 +1069,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, "$BASIC_FETCH_URL$bidRequest.accountId") assert floorsLogs.size() == 1 - assert floorsLogs[0].contains(URL_EMPTY_ERROR_LOG(bidRequest, message)) + assert floorsLogs[0].contains(FETCHING_FLOORS_ERROR_LOG(bidRequest, message)) and: "Floors validation failure cannot reject the entire auction" assert !response.seatbid?.isEmpty() @@ -1319,7 +1401,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" def message = "Price floor floorMin must be positive float, but was $invalidFloorMin" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] } def "PBS should validate rules from request when request doesn't contain modelGroups"() { @@ -1350,7 +1432,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" def message = "Price floor rules should contain at least one model group" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] } def "PBS should validate rules from request when request doesn't contain values"() { @@ -1380,7 +1462,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(PRICE_FLOOR_VALUES_MISSING)] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(PRICE_FLOOR_VALUES_MISSING)] } def "PBS should validate rules from request when modelWeight from request is invalid"() { @@ -1414,7 +1496,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(MODEL_WEIGHT_INVALID.formatted(invalidModelWeight))] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(MODEL_WEIGHT_INVALID.formatted(invalidModelWeight))] where: invalidModelWeight << [0, MAX_MODEL_WEIGHT + 1] @@ -1456,7 +1538,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(MODEL_WEIGHT_INVALID.formatted(invalidModelWeight))] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(MODEL_WEIGHT_INVALID.formatted(invalidModelWeight))] where: invalidModelWeight << [0, MAX_MODEL_WEIGHT + 1] @@ -1499,7 +1581,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" def message = "Price floor root skipRate must be in range(0-100), but was $invalidSkipRate" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] where: invalidSkipRate << [SKIP_RATE_MIN - 1, SKIP_RATE_MAX + 1] @@ -1542,7 +1624,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" def message = "Price floor data skipRate must be in range(0-100), but was $invalidSkipRate" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] where: invalidSkipRate << [SKIP_RATE_MIN - 1, SKIP_RATE_MAX + 1] @@ -1584,7 +1666,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(SKIP_RATE_INVALID.formatted(invalidSkipRate))] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(SKIP_RATE_INVALID.formatted(invalidSkipRate))] where: invalidSkipRate << [SKIP_RATE_MIN - 1, SKIP_RATE_MAX + 1] @@ -1623,7 +1705,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Response should contain warning" def message = "Price floor modelGroup default must be positive float, but was $invalidDefaultFloorValue" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] } def "PBS shouldn't emit error in log and response when floors is not in request and floors fetching disabled for account"() { @@ -1651,15 +1733,15 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "PBS shouldn't log a errors" def message = 'Price floor rules data must be present' def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert !floorsLogs.size() and: "PBS request on response object status should be in progress" - assert getRequests(bidResponse)[GENERIC.value]?.first?.ext?.prebid?.floors?.fetchStatus == INPROGRESS + assert getRequests(bidResponse)[GENERIC.value]?.first?.ext?.prebid?.floors?.fetchStatus == NONE and: "PBS bidderRequest status should be in progress" def bidderRequest = bidder.getBidderRequests(bidRequest.id) - assert bidderRequest.ext.prebid.floors.fetchStatus == [INPROGRESS] + assert bidderRequest.ext.prebid.floors.fetchStatus == [NONE] and: "Alerts.general metrics shouldn't be populated" def metrics = floorsPbsService.sendCollectedMetricsRequest() @@ -1669,7 +1751,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { bidRequest << [BidRequest.getDefaultBidRequest(), getBidRequestWithFloors().tap { it.ext.prebid.floors = null }] } - def "PBS should emit error in log and response when floor data is empty and floors fetching disabled for account and enabled for request"() { + def "PBS should emit error in log and response when floor data is empty and floors fetching disabled for account and enabled for request"() { given: "Default BidRequest with empty floors.data" def bidRequest = bidRequestWithFloors.tap { ext.prebid.floors.enabled = true @@ -1695,14 +1777,14 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { then: "PBS should log a warning" def message = 'Price floor rules data must be present' assert bidResponse.ext?.warnings[PREBID]*.code == [999] - assert bidResponse.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert bidResponse.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] and: "PBS should not add errors" assert !bidResponse.ext.errors and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert floorsLogs.size() == 1 and: "PBS request on response object status should be not in progress" @@ -1747,7 +1829,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "PBS shouldn't log a errors" def message = 'Price floor rules data must be present' def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert !floorsLogs.size() and: "Alerts.general metrics shouldn't be populated" @@ -1789,7 +1871,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "PBS shouldn't log a errors" def message = 'Price floor rules data must be present' def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert !floorsLogs.size() and: "PBS request on response object status should be in progress" @@ -1947,7 +2029,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, "$BASIC_FETCH_URL$bidRequest.accountId") assert floorsLogs.size() == 1 - assert floorsLogs[0].contains(URL_EMPTY_ERROR_LOG(bidRequest, MODEL_WEIGHT_INVALID.formatted(invalidModelWeight))) + assert floorsLogs[0].contains(FETCHING_FLOORS_ERROR_LOG(bidRequest, MODEL_WEIGHT_INVALID.formatted(invalidModelWeight))) and: "Floors validation failure cannot reject the entire auction" assert !response.seatbid?.isEmpty() @@ -2005,7 +2087,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, "$BASIC_FETCH_URL$bidRequest.accountId") assert floorsLogs.size() == 1 - assert floorsLogs[0].contains(URL_EMPTY_ERROR_LOG(bidRequest, message)) + assert floorsLogs[0].contains(FETCHING_FLOORS_ERROR_LOG(bidRequest, message)) and: "Floors validation failure cannot reject the entire auction" assert !response.seatbid?.isEmpty() @@ -2062,7 +2144,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, "$BASIC_FETCH_URL$bidRequest.accountId") assert floorsLogs.size() == 1 - assert floorsLogs[0].contains(URL_EMPTY_ERROR_LOG(bidRequest, SKIP_RATE_INVALID.formatted(invalidSkipRate))) + assert floorsLogs[0].contains(FETCHING_FLOORS_ERROR_LOG(bidRequest, SKIP_RATE_INVALID.formatted(invalidSkipRate))) and: "Floors validation failure cannot reject the entire auction" assert !response.seatbid?.isEmpty() @@ -2120,7 +2202,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, "$BASIC_FETCH_URL$bidRequest.accountId") assert floorsLogs.size() == 1 - assert floorsLogs[0].contains(URL_EMPTY_ERROR_LOG(bidRequest, message)) + assert floorsLogs[0].contains(FETCHING_FLOORS_ERROR_LOG(bidRequest, message)) and: "Floors validation failure cannot reject the entire auction" assert !response.seatbid?.isEmpty() diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy index 393a8f8cec0..27eadff1330 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy @@ -17,7 +17,6 @@ import org.prebid.server.functional.model.request.auction.Video import org.prebid.server.functional.model.response.auction.BidResponse import org.prebid.server.functional.model.response.auction.MediaType import org.prebid.server.functional.util.PBSUtils -import spock.lang.IgnoreRest import java.time.Instant @@ -80,7 +79,7 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { and: "PBS shouldn't log a errors" def message = 'Price floor rules data must be present' def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert !floorsLogs.size() and: "Alerts.general metrics shouldn't be populated" @@ -607,7 +606,7 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { then: "PBS should log a warning" def message = "Price floor rules number ${getRuleSize(bidRequest)} exceeded its maximum number ${MAX_RULES_SIZE}" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] and: "Alerts.general metrics should be populated" def metrics = floorsPbsService.sendCollectedMetricsRequest() @@ -648,7 +647,7 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { then: "PBS should log a warning" def message = "Price floor schema dimensions ${getSchemaSize(bidRequest)} exceeded its maximum number ${MAX_SCHEMA_DIMENSIONS_SIZE}" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] and: "Alerts.general metrics should be populated" def metrics = floorsPbsService.sendCollectedMetricsRequest() @@ -695,11 +694,11 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { then: "PBS should log a warning" def message = "Price floor schema dimensions ${getSchemaSize(bidRequest)} exceeded its maximum number ${MAX_SCHEMA_DIMENSIONS_SIZE}" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert floorsLogs.size() == 1 and: "Metrics should be updated" @@ -748,11 +747,11 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { then: "Response should includer error warning" def message = "Price floor schema dimensions ${getSchemaSize(bidRequest)} exceeded its maximum number ${MAX_SCHEMA_DIMENSIONS_SIZE}" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] and: "PBS shouldn't log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert floorsLogs.size() == 1 and: "Metrics should be updated" @@ -789,11 +788,11 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { then: "PBS should log a warning" def message = "Price floor schema dimensions ${getSchemaSize(bidRequest)} exceeded its maximum number ${MAX_SCHEMA_DIMENSIONS_SIZE}" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" @@ -834,11 +833,11 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { def message = "Price floor schema dimensions ${floorSchemaFilesSize} " + "exceeded its maximum number ${MAX_SCHEMA_DIMENSIONS_SIZE}" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" @@ -907,12 +906,13 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { def message = "Price floor rules number ${getRuleSize(ampStoredRequest)} " + "exceeded its maximum number ${MAX_RULES_SIZE}" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, "Price Floors can't be resolved for account $ampRequest.account and " + - "request $ampStoredRequest.id, reason: ${FETCHING_DISABLED_WARNING_MESSAGE(message)}") + "request $ampStoredRequest.id, reason: Price floors processing failed: Fetching is disabled. " + + "Following parsing of request price floors is failed: $message") assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" @@ -944,11 +944,11 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { then: "PBS should log a warning" def message = "Price floor data skipRate must be in range(0-100), but was $requestSkipRate" assert bidResponse.ext?.warnings[PREBID]*.code == [999] - assert bidResponse.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert bidResponse.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" @@ -978,11 +978,11 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { then: "PBS should log a warning" def message = "Price floor rules should contain at least one model group" assert bidResponse.ext?.warnings[PREBID]*.code == [999] - assert bidResponse.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert bidResponse.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" @@ -1014,11 +1014,11 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { then: "PBS should log a warning" def message = "Price floor modelGroup modelWeight must be in range(1-100), but was $requestModelWeight" assert bidResponse.ext?.warnings[PREBID]*.code == [999] - assert bidResponse.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert bidResponse.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" @@ -1051,11 +1051,11 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { then: "PBS should log a warning" def message = "Price floor modelGroup skipRate must be in range(0-100), but was $requestModelGroupsSkipRate" assert bidResponse.ext?.warnings[PREBID]*.code == [999] - assert bidResponse.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert bidResponse.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] and: "PBS should log an errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" @@ -1086,11 +1086,11 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { then: "PBS should log a warning" def message = "Price floor modelGroup default must be positive float, but was $requestModelGroupsSkipRate" assert bidResponse.ext?.warnings[PREBID]*.code == [999] - assert bidResponse.ext?.warnings[PREBID]*.message == [FETCHING_DISABLED_WARNING_MESSAGE(message)] + assert bidResponse.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, FETCHING_DISABLED_ERROR_LOG(bidRequest, message)) + def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" @@ -1098,19 +1098,12 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { assert metrics[ALERT_GENERAL] == 1 } -// @IgnoreRest def "PBS should emit error in log and response when account have disabled dynamic data config"() { given: "Bid request without floors" def bidRequest = getBidRequestWithFloors().tap { ext.prebid.floors.data = null } - def floorValue = PBSUtils.randomFloorValue - def floorsResponse = PriceFloorData.priceFloorData.tap { - modelGroups[0].values = [(rule): floorValue] - useFetchDataRate = -100 - } - and: "Account with disabled dynamic data" def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { config.auction.priceFloors.useDynamicData = false @@ -1131,13 +1124,13 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { def response = floorsPbsService.sendAuctionRequest(bidRequest) then: "PBS should log a warning" + def message = 'Price floor rules data must be present' assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == ['Using dynamic data is not allowed'] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE('Price floor rules data must be present')] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime) - def floorsLogs = getLogsByText(logs, "No price floor data for account ${bidRequest.accountId} " + - "and request ${bidRequest.id}, reason: Using dynamic data is not allowed") + def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, 'Using dynamic data is not allowed', message)) assert floorsLogs.size() == 1 and: "Alerts.general metrics should be populated" From 01e899354a9229f43850568b655a8f07047d7278 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Thu, 8 May 2025 11:17:19 +0300 Subject: [PATCH 7/9] update tests --- .../PriceFloorsFetchingSpec.groovy | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy index 57e21a4e3af..733ac70a700 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy @@ -1751,16 +1751,16 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { bidRequest << [BidRequest.getDefaultBidRequest(), getBidRequestWithFloors().tap { it.ext.prebid.floors = null }] } - def "PBS should emit error in log and response when floor data is empty and floors fetching disabled for account and enabled for request"() { + def "PBS should emit error in log and response when floor data is empty and floors fetching disabled for account and #requestFloors for request"() { given: "Default BidRequest with empty floors.data" def bidRequest = bidRequestWithFloors.tap { - ext.prebid.floors.enabled = true - ext.prebid.floors.data = null + it.ext.prebid.floors.enabled = requestFloors + it.ext.prebid.floors.data = null } and: "Account with disabled fetching" def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { - config.auction.priceFloors.fetch.enabled = false + it.config.auction.priceFloors.fetch.enabled = false } accountDao.save(account) @@ -1797,18 +1797,21 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Alerts.general metrics should be populated" def metrics = floorsPbsService.sendCollectedMetricsRequest() assert metrics[ALERT_GENERAL] == 1 + + where: + requestFloors << [null, true] } - def "PBS shouldn't emit error in log and response when floor data is empty and floors fetching disabled for account and #requestFloors for request"() { + def "PBS shouldn't emit error in log and response when floor data is empty and floors fetching disabled for account and disabled for request"() { given: "Default BidRequest with empty floors.data" def bidRequest = bidRequestWithFloors.tap { - ext.prebid.floors.enabled = requestFloors - ext.prebid.floors.data = null + it.ext.prebid.floors.enabled = false + it.ext.prebid.floors.data = null } and: "Account with disabled fetching" def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { - config.auction.priceFloors.fetch.enabled = false + it.config.auction.priceFloors.fetch.enabled = false } accountDao.save(account) @@ -1835,9 +1838,6 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { and: "Alerts.general metrics shouldn't be populated" def metrics = floorsPbsService.sendCollectedMetricsRequest() assert !metrics[ALERT_GENERAL] - - where: - requestFloors << [null, false] } def "PBS shouldn't emit error in log and response when data is invalid and floors fetching enabled for account"() { From 086875440372c119800a8849fafc7754a00c0a70 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Mon, 19 May 2025 00:18:39 +0300 Subject: [PATCH 8/9] update after review --- .../PriceFloorsFetchingSpec.groovy | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy index 733ac70a700..b8cbd595f2e 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsFetchingSpec.groovy @@ -1731,7 +1731,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { assert !bidResponse.ext?.errors and: "PBS shouldn't log a errors" - def message = 'Price floor rules data must be present' + def message = "Price floor rules data must be present" def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert !floorsLogs.size() @@ -1751,10 +1751,10 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { bidRequest << [BidRequest.getDefaultBidRequest(), getBidRequestWithFloors().tap { it.ext.prebid.floors = null }] } - def "PBS should emit error in log and response when floor data is empty and floors fetching disabled for account and #requestFloors for request"() { + def "PBS should emit error in log and response when floor data is empty and floors fetching disabled for account and #requestFloorEnabled for request"() { given: "Default BidRequest with empty floors.data" def bidRequest = bidRequestWithFloors.tap { - it.ext.prebid.floors.enabled = requestFloors + it.ext.prebid.floors.enabled = requestFloorEnabled it.ext.prebid.floors.data = null } @@ -1775,7 +1775,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def bidResponse = floorsPbsService.sendAuctionRequest(bidRequest) then: "PBS should log a warning" - def message = 'Price floor rules data must be present' + def message = "Price floor rules data must be present" assert bidResponse.ext?.warnings[PREBID]*.code == [999] assert bidResponse.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] @@ -1799,10 +1799,10 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { assert metrics[ALERT_GENERAL] == 1 where: - requestFloors << [null, true] + requestFloorEnabled << [null, true] } - def "PBS shouldn't emit error in log and response when floor data is empty and floors fetching disabled for account and disabled for request"() { + def "PBS shouldn't emit error in log and response when floor data is empty and floors fetching disabled for account and floors disabled for request"() { given: "Default BidRequest with empty floors.data" def bidRequest = bidRequestWithFloors.tap { it.ext.prebid.floors.enabled = false @@ -1830,7 +1830,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { assert !bidResponse.ext?.errors and: "PBS shouldn't log a errors" - def message = 'Price floor rules data must be present' + def message = "Price floor rules data must be present" def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert !floorsLogs.size() @@ -1843,15 +1843,12 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { def "PBS shouldn't emit error in log and response when data is invalid and floors fetching enabled for account"() { given: "Default BidRequest with empty floors.data" def bidRequest = bidRequestWithFloors.tap { - ext.prebid.floors.enabled = requestFloors + ext.prebid.floors.enabled = requestEnabledFloors ext.prebid.floors.data = null } and: "Account with enabled fetching" - def account = getAccountWithEnabledFetch(bidRequest.accountId).tap { - config.auction.priceFloors.enabled = true - config.auction.priceFloors.fetch.enabled = true - } + def account = getAccountWithEnabledFetch(bidRequest.accountId) accountDao.save(account) and: "Default bid response" @@ -1869,7 +1866,7 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { assert !bidResponse.ext?.errors and: "PBS shouldn't log a errors" - def message = 'Price floor rules data must be present' + def message = "Price floor rules data must be present" def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert !floorsLogs.size() @@ -1886,13 +1883,12 @@ class PriceFloorsFetchingSpec extends PriceFloorsBaseSpec { assert !metrics[ALERT_GENERAL] where: - requestFloors << [null, true] + requestEnabledFloors << [null, true] } - def "PBS shouldn't emit error in log and response when data is invalid and floors disabled for request"() { + def "PBS shouldn't emit error in log and response when data is invalid and floors disabled for account"() { given: "Default BidRequest with empty floors.data" def bidRequest = bidRequestWithFloors.tap { - ext.prebid.floors.enabled = false ext.prebid.floors.data = null } From ca8193d497c739f6476cefe8639ba974b98598d5 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Mon, 19 May 2025 13:21:39 +0300 Subject: [PATCH 9/9] update after review --- .../tests/pricefloors/PriceFloorsSignalingSpec.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy index 27eadff1330..7d430e37f78 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsSignalingSpec.groovy @@ -77,7 +77,7 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { assert !response.ext.errors and: "PBS shouldn't log a errors" - def message = 'Price floor rules data must be present' + def message = "Price floor rules data must be present" def logs = floorsPbsService.getLogsByTime(startTime) def floorsLogs = getLogsByText(logs, PRICE_FLOORS_ERROR_LOG(bidRequest, FETCHING_DISABLED_ERROR, message)) assert !floorsLogs.size() @@ -1124,9 +1124,9 @@ class PriceFloorsSignalingSpec extends PriceFloorsBaseSpec { def response = floorsPbsService.sendAuctionRequest(bidRequest) then: "PBS should log a warning" - def message = 'Price floor rules data must be present' + def message = "Price floor rules data must be present" assert response.ext?.warnings[PREBID]*.code == [999] - assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE('Price floor rules data must be present')] + assert response.ext?.warnings[PREBID]*.message == [WARNING_MESSAGE(message)] and: "PBS should log a errors" def logs = floorsPbsService.getLogsByTime(startTime)