From 6ad7fd4847352c9baa0effd9ca94b794558787e1 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Tue, 13 May 2025 21:42:49 +0300 Subject: [PATCH 1/3] Test: Invalid GPP string for request handling --- .../privacy/GppSyncUserActivitiesSpec.groovy | 38 +++++--- .../GppTransmitEidsActivitiesSpec.groovy | 45 ++++++---- .../GppTransmitUfpdActivitiesSpec.groovy | 90 ++++++++++++------- .../tests/privacy/PrivacyBaseSpec.groovy | 2 +- 4 files changed, 114 insertions(+), 61 deletions(-) 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..83806a71981 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 @@ -489,7 +489,7 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap { it.gppSid = US_NAT_V1.value it.account = accountId - it.gpp = INVALID_GPP_STRING + it.gpp = invalidGpp } and: "Activities set for cookie sync with allowing privacy regulation" @@ -506,14 +506,21 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) accountDao.save(account) + and: "Flush metrics" + flushMetrics(activityPbsService) + when: "PBS processes cookie sync request" def response = activityPbsService.sendCookieSyncRequest(cookieSyncRequest) - then: "Response should not contain any URLs for bidders" - assert !response.bidderStatus.userSync.url + then: "Response should contain bidders userSync.urls" + assert response.getBidderUserSync(GENERIC).userSync.url - and: "Response should not contain any warning" - assert !response.warnings + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(cookieSyncRequest, SYNC_USER)] == 1 + + where: + invalidGpp << [null, "", PBSUtils.randomString, INVALID_GPP_STRING] } def "PBS cookie sync call when request have different gpp consent but match and rejecting should exclude bidders URLs"() { @@ -1365,7 +1372,7 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { def setuidRequest = SetuidRequest.defaultSetuidRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value - it.gpp = INVALID_GPP_STRING + it.gpp = invalidGpp } and: "UIDS Cookie" @@ -1385,13 +1392,22 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) accountDao.save(account) + and: "Flush metrics" + flushMetrics(activityPbsService) + when: "PBS processes cookie sync request" - activityPbsService.sendSetUidRequest(setuidRequest, uidsCookie) + def response = activityPbsService.sendSetUidRequest(setuidRequest, uidsCookie) - then: "Request should fail with error" - def exception = thrown(PrebidServerException) - assert exception.statusCode == INVALID_STATUS_CODE - assert exception.responseBody == INVALID_STATUS_MESSAGE + then: "Response should contain uids cookie" + assert response.uidsCookie + assert response.responseBody + + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(setuidRequest, SYNC_USER)] == 1 + + where: + invalidGpp << [null, "", PBSUtils.randomString, INVALID_GPP_STRING] } def "PBS setuid request when request have different gpp consent but match and rejecting 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..95f24416e19 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 @@ -77,7 +77,6 @@ import static org.prebid.server.functional.model.request.auction.PrivacyModule.I import static org.prebid.server.functional.model.request.auction.PrivacyModule.IAB_US_CUSTOM_LOGIC import static org.prebid.server.functional.model.request.auction.PrivacyModule.IAB_US_GENERAL import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE -import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID import static org.prebid.server.functional.util.privacy.model.State.ALABAMA import static org.prebid.server.functional.util.privacy.model.State.ONTARIO @@ -848,7 +847,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def bidRequest = getBidRequestWithPersonalData(accountId).tap { regs.gppSid = [US_NAT_V1.intValue] - regs.gpp = INVALID_GPP_STRING + regs.gpp = invalidGpp } and: "Activities set for transmitEIDS with rejecting privacy regulation" @@ -865,21 +864,29 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) accountDao.save(account) + and: "Flush metrics" + flushMetrics(activityPbsService) + when: "PBS processes auction requests" def response = activityPbsService.sendAuctionRequest(bidRequest) - then: "Generic bidder request should have empty EIDS fields" + then: "Generic bidder request should have data in EIDS fields" def genericBidderRequest = bidder.getBidderRequest(bidRequest.id) - verifyAll { - !genericBidderRequest.user.eids - !genericBidderRequest.user?.ext?.eids - } + assert genericBidderRequest.user.eids[0].source == bidRequest.user.eids[0].source + + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(bidRequest, TRANSMIT_EIDS)] == 1 + assert metrics[ACCOUNT_PROCESSED_RULES_COUNT.getValue(bidRequest, TRANSMIT_EIDS)] == 1 and: "Response should not contain any warnings" assert !response.ext.warnings and: "Response should not contain any errors" assert !response.ext.errors + + where: + invalidGpp << [null, "", PBSUtils.randomString, INVALID_GPP_STRING] } def "PBS auction call when request have different gpp consent but match and rejecting should remove EIDS fields in request"() { @@ -1879,7 +1886,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value - it.consentString = INVALID_GPP_STRING + it.consentString = invalidGpp it.consentType = GPP } @@ -1901,22 +1908,28 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) storedRequestDao.save(storedRequest) + and: "Flush metrics" + flushMetrics(activityPbsService) + when: "PBS processes amp request" def response = activityPbsService.sendAmpRequest(ampRequest) - then: "Generic bidder request should have empty EIDS fields" + then: "Generic bidder request should have data in EIDS fields" def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) - verifyAll { - !genericBidderRequest.user.eids - !genericBidderRequest.user?.ext?.eids - } + assert genericBidderRequest.user.eids[0].source == ampStoredRequest.user.eids[0].source + + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(ampStoredRequest, TRANSMIT_EIDS)] == 1 and: "Response should not contain any warnings" assert !response.ext.warnings - and: "Response should contain amp error" - assert response.ext?.errors[PREBID]*.code == [999] - assert response.ext?.errors[PREBID]*.message == ["Amp request parameter consent_string has invalid format: $INVALID_GPP_STRING"] + and: "Response should not contain any errors" + assert !response.ext.errors + + where: + invalidGpp << [null, "", PBSUtils.randomString, INVALID_GPP_STRING] } def "PBS amp call when request have different gpp consent but match and rejecting should remove EIDS fields in request"() { 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..e037940a660 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 @@ -1114,7 +1114,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def bidRequest = getBidRequestWithPersonalData(accountId).tap { regs.gppSid = [US_NAT_V1.intValue] - regs.gpp = INVALID_GPP_STRING + regs.gpp = invalidGpp } and: "Activities set for transmitUfpd with rejecting privacy regulation" @@ -1131,25 +1131,30 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) accountDao.save(account) + and: "Flush metrics" + flushMetrics(activityPbsService) + when: "PBS processes auction requests" def response= activityPbsService.sendAuctionRequest(bidRequest) - then: "Generic bidder request should have empty UFPD fields" + then: "Generic bidder request should have data in UFPD fields" def bidderRequest = bidder.getBidderRequest(bidRequest.id) + verifyAll { - !bidderRequest.device.didsha1 - !bidderRequest.device.didmd5 - !bidderRequest.device.dpidsha1 - !bidderRequest.device.ifa - !bidderRequest.device.macsha1 - !bidderRequest.device.macmd5 - !bidderRequest.device.dpidmd5 - !bidderRequest.user.id - !bidderRequest.user.buyeruid - !bidderRequest.user.yob - !bidderRequest.user.gender - !bidderRequest.user.data - !bidderRequest.user.ext + bidderRequest.device.didsha1 == bidRequest.device.didsha1 + bidderRequest.device.didmd5 == bidRequest.device.didmd5 + bidderRequest.device.dpidsha1 == bidRequest.device.dpidsha1 + bidderRequest.device.ifa == bidRequest.device.ifa + bidderRequest.device.macsha1 == bidRequest.device.macsha1 + bidderRequest.device.macmd5 == bidRequest.device.macmd5 + bidderRequest.device.dpidmd5 == bidRequest.device.dpidmd5 + bidderRequest.user.id == bidRequest.user.id + bidderRequest.user.buyeruid == bidRequest.user.buyeruid + bidderRequest.user.yob == bidRequest.user.yob + bidderRequest.user.gender == bidRequest.user.gender + bidderRequest.user.data == bidRequest.user.data + bidderRequest.user.geo.zip == bidRequest.user.geo.zip + bidderRequest.user.ext.data.buyeruid == bidRequest.user.ext.data.buyeruid } and: "Generic bidder request should have data in EIDS fields" @@ -1160,6 +1165,14 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Response should not contain any errors" assert !response.ext.errors + + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(bidRequest, TRANSMIT_UFPD)] == 1 + assert metrics[ACCOUNT_PROCESSED_RULES_COUNT.getValue(bidRequest, TRANSMIT_UFPD)] == 1 + + where: + invalidGpp << [null, "", PBSUtils.randomString, INVALID_GPP_STRING] } def "PBS auction call when request have different gpp consent but match and rejecting should remove UFPD fields in request"() { @@ -2444,7 +2457,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value - it.consentString = INVALID_GPP_STRING + it.consentString = invalidGpp it.consentType = GPP } @@ -2466,36 +2479,47 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) storedRequestDao.save(storedRequest) + and: "Flush metrics" + flushMetrics(activityPbsService) + when: "PBS processes amp request" def response = activityPbsService.sendAmpRequest(ampRequest) - then: "Generic bidder request should have empty UFPD fields" + then: "Generic bidder request should have data in UFPD fields" def bidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + verifyAll { - !bidderRequest.device.didsha1 - !bidderRequest.device.didmd5 - !bidderRequest.device.dpidsha1 - !bidderRequest.device.ifa - !bidderRequest.device.macsha1 - !bidderRequest.device.macmd5 - !bidderRequest.device.dpidmd5 - !bidderRequest.user.id - !bidderRequest.user.buyeruid - !bidderRequest.user.yob - !bidderRequest.user.gender - !bidderRequest.user.data - !bidderRequest.user.ext + bidderRequest.device.didsha1 == ampStoredRequest.device.didsha1 + bidderRequest.device.didmd5 == ampStoredRequest.device.didmd5 + bidderRequest.device.dpidsha1 == ampStoredRequest.device.dpidsha1 + bidderRequest.device.ifa == ampStoredRequest.device.ifa + bidderRequest.device.macsha1 == ampStoredRequest.device.macsha1 + bidderRequest.device.macmd5 == ampStoredRequest.device.macmd5 + bidderRequest.device.dpidmd5 == ampStoredRequest.device.dpidmd5 + bidderRequest.user.id == ampStoredRequest.user.id + bidderRequest.user.buyeruid == ampStoredRequest.user.buyeruid + bidderRequest.user.yob == ampStoredRequest.user.yob + bidderRequest.user.gender == ampStoredRequest.user.gender + bidderRequest.user.data == ampStoredRequest.user.data + bidderRequest.user.geo == ampStoredRequest.user.geo + bidderRequest.user.ext.data.buyeruid == ampStoredRequest.user.ext.data.buyeruid } and: "Generic bidder request should have data in EIDS fields" assert bidderRequest.user.eids == ampStoredRequest.user.eids + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(ampStoredRequest, TRANSMIT_UFPD)] == 1 + and: "Response should not contain any warnings" assert !response.ext.warnings - and: "Response should contain amp error" - assert response.ext?.errors[PREBID]*.code == [999] - assert response.ext?.errors[PREBID]*.message == ["Amp request parameter consent_string has invalid format: $INVALID_GPP_STRING"] + and: "Response should not contain any errors" + assert !response.ext.errors + + where: + invalidGpp << [null, "", PBSUtils.randomString, INVALID_GPP_STRING] } def "PBS amp call when request have different gpp consent but match and rejecting should remove UFPD fields in request"() { diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/PrivacyBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/PrivacyBaseSpec.groovy index aff17c4d49b..04765af3647 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/PrivacyBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/PrivacyBaseSpec.groovy @@ -95,7 +95,7 @@ abstract class PrivacyBaseSpec extends BaseSpec { private static final Map GDPR_EEA_COUNTRY = ["gdpr.eea-countries": "$BULGARIA.ISOAlpha2, SK, VK" as String] protected static final String VENDOR_LIST_PATH = "/app/prebid-server/data/vendorlist-v{VendorVersion}/{VendorVersion}.json" - protected static final String INVALID_GPP_STRING = "DBABLA~BVQqAAAAAg.YA" // TODO replace BVQqAAAAAg with ${PBSUtils.getRandomString(7)} when proper fix is ready + protected static final String INVALID_GPP_STRING = "DBABLA~${PBSUtils.getRandomString(7)}.YA" protected static final String VALID_VALUE_FOR_GPC_HEADER = "1" protected static final GppConsent SIMPLE_GPC_DISALLOW_LOGIC = new UsNatV1Consent.Builder().setGpc(true).build() protected static final VendorList vendorListResponse = new VendorList(networkServiceContainer) From 627daa7af6aa100804214435b7c65ee4a1b7732b Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Sun, 18 May 2025 23:30:02 +0300 Subject: [PATCH 2/3] Test: Increase GPP tests coverage --- .../privacy/GppSyncUserActivitiesSpec.groovy | 159 ++++++-- .../GppTransmitEidsActivitiesSpec.groovy | 276 ++++++++++++-- .../GppTransmitUfpdActivitiesSpec.groovy | 356 ++++++++++++++++-- .../tests/privacy/PrivacyBaseSpec.groovy | 3 +- 4 files changed, 696 insertions(+), 98 deletions(-) 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 83806a71981..0c7c9baa26b 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 @@ -31,7 +31,6 @@ import org.prebid.server.functional.util.privacy.gpp.data.UsUtahSensitiveData import java.time.Instant -import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST import static org.prebid.server.functional.model.bidder.BidderName.GENERIC import static org.prebid.server.functional.model.config.DataActivity.CONSENT import static org.prebid.server.functional.model.config.DataActivity.NOTICE_NOT_PROVIDED @@ -483,9 +482,59 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { ] } - def "PBS cookie sync call when privacy module contain invalid GPP string should exclude bidders URLs"() { + def "PBS cookie sync call when privacy module contain invalid GPP segment should respond with required bidder URL and emit error log"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Cookie sync request with link to account" + def accountId = PBSUtils.randomString + def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap { + it.gppSid = US_NAT_V1.value + it.account = accountId + it.gpp = INVALID_GPP_STRING + } + + and: "Activities set for cookie sync with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(SYNC_USER, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration" + def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true) + + and: "Existed account with cookie sync and privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + when: "PBS processes cookie sync request" + def response = activityPbsService.sendCookieSyncRequest(cookieSyncRequest) + + then: "Response should contain bidders userSync.urls" + assert response.getBidderUserSync(GENERIC).userSync.url + + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(cookieSyncRequest, SYNC_USER)] == 1 + assert metrics[ALERT_GENERAL.value] == 1 + + and: "Response shouldn't contain warnings" + assert !response.warnings + + and: "Logs should contain error" + def logs = activityPbsService.getLogsByTime(startTime) + assert getLogsByText(logs, "UsNat privacy module creation failed: Unable to decode UsNatCoreSegment " + + "'${INVALID_GPP_SEGMENT}'. Activity: SYNC_USER. Section: ${US_NAT_V1.value}. Gpp: $INVALID_GPP_STRING").size() == 1 + } + + def "PBS cookie sync call when privacy module contain invalid GPP string should respond with required bidder URL and emit warning in response"() { given: "Cookie sync request with link to account" def accountId = PBSUtils.randomString + def invalidGpp = PBSUtils.randomString def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap { it.gppSid = US_NAT_V1.value it.account = accountId @@ -519,8 +568,8 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { def metrics = activityPbsService.sendCollectedMetricsRequest() assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(cookieSyncRequest, SYNC_USER)] == 1 - where: - invalidGpp << [null, "", PBSUtils.randomString, INVALID_GPP_STRING] + and: "Should add a warning when in debug mode" + assert response.warnings == ["GPP string invalid: Unable to decode '$invalidGpp'"] } def "PBS cookie sync call when request have different gpp consent but match and rejecting should exclude bidders URLs"() { @@ -626,8 +675,11 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { then: "Response should contain bidders userSync.urls" assert response.getBidderUserSync(GENERIC).userSync.url + and: "Response shouldn't contain warnings" + assert !response.warnings + where: - regsGpp << ["", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] + regsGpp << [null, "", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] } def "PBS cookie sync call when privacy regulation have duplicate should include proper responded with bidders URLs"() { @@ -783,7 +835,10 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { } def "PBS cookie sync call when custom privacy regulation empty and normalize is disabled should respond with an error and update metric"() { - given: "Generic BidRequest with gpp and account setup" + given: "Test start time" + def startTime = Instant.now() + + and: "Generic BidRequest with gpp and account setup" def gppConsent = new UsNatV1Consent.Builder().setGpc(true).build() def accountId = PBSUtils.randomNumber as String def cookieSyncRequest = CookieSyncRequest.defaultCookieSyncRequest.tap { @@ -814,17 +869,18 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { accountDao.save(account) when: "PBS processes auction requests" - activityPbsService.sendCookieSyncRequest(cookieSyncRequest) + def response = activityPbsService.sendCookieSyncRequest(cookieSyncRequest) - then: "Response should contain error" - def error = thrown(PrebidServerException) - assert error.statusCode == BAD_REQUEST.code() - assert error.responseBody == "Invalid account configuration: JsonLogic exception: " + - "objects must have exactly 1 key defined, found 0" + then: "Response should contain bidders userSync.urls" + assert response.getBidderUserSync(GENERIC).userSync.url and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() assert metrics[ALERT_GENERAL.getValue()] == 1 + + and: "Logs should contain error" + def logs = activityPbsService.getLogsByTime(startTime) + assert getLogsByText(logs, 'USCustomLogic creation failed: objects must have exactly 1 key defined, found 0').size() == 1 } def "PBS cookie sync when custom privacy regulation with normalizing should exclude bidders URLs"() { @@ -1366,13 +1422,16 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { ] } - def "PBS setuid request when privacy module contain invalid GPP string should reject bidders with status code invalidStatusCode"() { - given: "Cookie sync SetuidRequest with accountId" + def "PBS setuid request when privacy module contain invalid GPP segment should respond with valid bidders UIDs cookies"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Cookie sync SetuidRequest with accountId" def accountId = PBSUtils.randomString def setuidRequest = SetuidRequest.defaultSetuidRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value - it.gpp = invalidGpp + it.gpp = INVALID_GPP_STRING } and: "UIDS Cookie" @@ -1405,9 +1464,53 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { and: "Metrics processed across activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(setuidRequest, SYNC_USER)] == 1 + assert metrics[ALERT_GENERAL.value] == 1 - where: - invalidGpp << [null, "", PBSUtils.randomString, INVALID_GPP_STRING] + and: "Logs should contain error" + def logs = activityPbsService.getLogsByTime(startTime) + assert getLogsByText(logs, "UsNat privacy module creation failed: Unable to decode UsNatCoreSegment " + + "'${INVALID_GPP_SEGMENT}'. Activity: SYNC_USER. Section: ${US_NAT_V1.value}. Gpp: $INVALID_GPP_STRING").size() == 1 + } + + def "PBS setuid request when privacy module contain invalid GPP string should respond with valid bidders UIDs cookies"() { + given: "Cookie sync SetuidRequest with accountId" + def accountId = PBSUtils.randomString + def setuidRequest = SetuidRequest.defaultSetuidRequest.tap { + it.account = accountId + it.gppSid = US_NAT_V1.value + it.gpp = PBSUtils.randomString + } + + and: "UIDS Cookie" + def uidsCookie = UidsCookie.defaultUidsCookie + + and: "Activities set for cookie sync with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(SYNC_USER, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration" + def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true) + + and: "Existed account with cookie sync and allow activities setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + when: "PBS processes cookie sync request" + def response = activityPbsService.sendSetUidRequest(setuidRequest, uidsCookie) + + then: "Response should contain uids cookie" + assert response.uidsCookie + assert response.responseBody + + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(setuidRequest, SYNC_USER)] == 1 } def "PBS setuid request when request have different gpp consent but match and rejecting should reject bidders with status code invalidStatusCode"() { @@ -1526,7 +1629,7 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { assert response.responseBody where: - regsGpp << ["", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] + regsGpp << [null, "", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] } def "PBS setuid request when privacy regulation have duplicate should respond with valid bidders UIDs cookies"() { @@ -1696,8 +1799,11 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { new EqualityValueRule(PERSONAL_DATA_CONSENTS, NOTICE_NOT_PROVIDED)] } - def "PBS setuid call when custom privacy regulation empty and normalize is disabled should respond with an error and update metric"() { - given: "Cookie sync SetuidRequest with accountId" + def "PBS setuid call when custom privacy regulation empty and normalize is disabled should respond with required UIDs cookies"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Cookie sync SetuidRequest with accountId" def accountId = PBSUtils.randomString def gppConsent = new UsNatV1Consent.Builder().setGpc(true).build() def setuidRequest = SetuidRequest.defaultSetuidRequest.tap { @@ -1731,17 +1837,18 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { accountDao.save(account) when: "PBS processes setuid request" - activityPbsService.sendSetUidRequest(setuidRequest, uidsCookie) + def response = activityPbsService.sendSetUidRequest(setuidRequest, uidsCookie) - then: "Response should contain error" - def error = thrown(PrebidServerException) - assert error.statusCode == BAD_REQUEST.code() - assert error.responseBody == "Invalid account configuration: JsonLogic exception: " + - "objects must have exactly 1 key defined, found 0" + then: "Response should contain uids cookie" + assert response.responseBody and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() assert metrics[ALERT_GENERAL.getValue()] == 1 + + and: "Logs should contain error" + def logs = activityPbsService.getLogsByTime(startTime) + assert getLogsByText(logs, 'USCustomLogic creation failed: objects must have exactly 1 key defined, found 0').size() == 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 95f24416e19..ab4bd92a93e 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 @@ -29,7 +29,6 @@ import org.prebid.server.functional.util.privacy.gpp.data.UsUtahSensitiveData import java.time.Instant -import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST import static io.netty.handler.codec.http.HttpResponseStatus.UNAUTHORIZED import static org.prebid.server.functional.model.config.DataActivity.CONSENT import static org.prebid.server.functional.model.config.DataActivity.NOTICE_NOT_PROVIDED @@ -77,6 +76,7 @@ import static org.prebid.server.functional.model.request.auction.PrivacyModule.I import static org.prebid.server.functional.model.request.auction.PrivacyModule.IAB_US_CUSTOM_LOGIC import static org.prebid.server.functional.model.request.auction.PrivacyModule.IAB_US_GENERAL import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE +import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID import static org.prebid.server.functional.util.privacy.model.State.ALABAMA import static org.prebid.server.functional.util.privacy.model.State.ONTARIO @@ -193,7 +193,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { when: "PBS processes auction requests" activityPbsService.sendAuctionRequest(bidRequest) - then: "Response should contain error" + then: "Logs should contain error" def logs = activityPbsService.getLogsByTime(startTime) assert getLogsByText(logs, "Activity configuration for account ${accountId} " + "contains conditional rule with empty array").size() == 1 @@ -842,12 +842,15 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { ] } - def "PBS auction call when privacy module contain invalid GPP string should remove EIDS fields in request"() { - given: "Default Generic BidRequests with EIDS fields and account id" + def "PBS auction call when privacy module contain invalid GPP segment shouldn't remove EIDS fields in request and emit error log"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default Generic BidRequests with EIDS fields and account id" def accountId = PBSUtils.randomNumber as String def bidRequest = getBidRequestWithPersonalData(accountId).tap { regs.gppSid = [US_NAT_V1.intValue] - regs.gpp = invalidGpp + regs.gpp = INVALID_GPP_STRING } and: "Activities set for transmitEIDS with rejecting privacy regulation" @@ -874,19 +877,68 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def genericBidderRequest = bidder.getBidderRequest(bidRequest.id) assert genericBidderRequest.user.eids[0].source == bidRequest.user.eids[0].source + and: "Response should not contain any warnings" + assert !response.ext.warnings + + and: "Response should not contain any errors" + assert !response.ext.errors + and: "Metrics processed across activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(bidRequest, TRANSMIT_EIDS)] == 1 assert metrics[ACCOUNT_PROCESSED_RULES_COUNT.getValue(bidRequest, TRANSMIT_EIDS)] == 1 + assert metrics[ALERT_GENERAL.value] == 1 - and: "Response should not contain any warnings" - assert !response.ext.warnings + and: "Logs should contain error" + def logs = activityPbsService.getLogsByTime(startTime) + assert getLogsByText(logs, "UsNat privacy module creation failed: Unable to decode UsNatCoreSegment " + + "'${INVALID_GPP_SEGMENT}'. Activity: TRANSMIT_EIDS. Section: ${US_NAT_V1.value}. Gpp: $INVALID_GPP_STRING").size() == 1 + } + + def "PBS auction call when privacy module contain invalid GPP string shouldn't remove EIDS fields in request and emit warning in response"() { + given: "Default Generic BidRequests with EIDS fields and account id" + def accountId = PBSUtils.randomNumber as String + def invalidGpp = PBSUtils.randomString + def bidRequest = getBidRequestWithPersonalData(accountId).tap { + regs.gppSid = [US_NAT_V1.intValue] + regs.gpp = invalidGpp + } + + and: "Activities set for transmitEIDS with rejecting privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration" + def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + when: "PBS processes auction requests" + def response = activityPbsService.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(bidRequest.id) + assert genericBidderRequest.user.eids[0].source == bidRequest.user.eids[0].source + + and: "Should add a warning when in debug mode" + assert response.ext.warnings[PREBID]?.code == [999] + assert response.ext.warnings[PREBID]?.message == ["GPP string invalid: Unable to decode '$invalidGpp'"] and: "Response should not contain any errors" assert !response.ext.errors - where: - invalidGpp << [null, "", PBSUtils.randomString, INVALID_GPP_STRING] + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(bidRequest, TRANSMIT_EIDS)] == 1 + assert metrics[ACCOUNT_PROCESSED_RULES_COUNT.getValue(bidRequest, TRANSMIT_EIDS)] == 1 } def "PBS auction call when request have different gpp consent but match and rejecting should remove EIDS fields in request"() { @@ -987,14 +1039,20 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { accountDao.save(account) when: "PBS processes auction requests" - activityPbsService.sendAuctionRequest(bidRequest) + def response = activityPbsService.sendAuctionRequest(bidRequest) then: "Generic bidder request should have data in EIDS fields" def genericBidderRequest = bidder.getBidderRequest(bidRequest.id) assert genericBidderRequest.user.eids[0].source == bidRequest.user.eids[0].source + and: "Response shouldn't contain errors" + assert !response.ext.errors + + and: "Response shouldn't contain warnings" + assert !response.ext.warnings + where: - regsGpp << ["", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] + regsGpp << [null, "", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] } def "PBS auction call when privacy regulation have duplicate should leave EIDS fields in request and update alerts metrics"() { @@ -1151,8 +1209,11 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { new EqualityValueRule(PERSONAL_DATA_CONSENTS, NOTICE_NOT_PROVIDED)] } - def "PBS auction call when custom privacy regulation empty and normalize is disabled should respond with an error and update metric"() { - given: "Generic BidRequest with gpp and account setup" + def "PBS auction call when custom privacy regulation empty and normalize is disabled should leave EIDS fields in request and emit error log"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Generic BidRequest with gpp and account setup" def gppConsent = new UsNatV1Consent.Builder().setGpc(true).build() def accountId = PBSUtils.randomNumber as String def bidRequest = getBidRequestWithPersonalData(accountId).tap { @@ -1183,16 +1244,25 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { accountDao.save(account) when: "PBS processes auction requests" - activityPbsService.sendAuctionRequest(bidRequest) + def response = activityPbsService.sendAuctionRequest(bidRequest) - then: "Response should contain error" - def error = thrown(PrebidServerException) - assert error.statusCode == BAD_REQUEST.code() - assert error.responseBody == "JsonLogic exception: objects must have exactly 1 key defined, found 0" + then: "Response should not contain any warnings" + assert !response.ext.warnings + + and: "Response should not contain any errors" + assert !response.ext.errors and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() assert metrics[ALERT_GENERAL.getValue()] == 1 + + and: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(bidRequest.id) + assert genericBidderRequest.user.eids[0].source == bidRequest.user.eids[0].source + + and: "Logs should contain error" + def logs = activityPbsService.getLogsByTime(startTime) + assert getLogsByText(logs, "USCustomLogic creation failed: objects must have exactly 1 key defined, found 0").size() == 1 } def "PBS auction call when custom privacy regulation with normalizing that match custom config should have empty EIDS fields"() { @@ -1445,7 +1515,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { when: "PBS processes amp request" activityPbsService.sendAmpRequest(ampRequest) - then: "Response should contain error" + then: "Logs should contain error" def logs = activityPbsService.getLogsByTime(startTime) assert getLogsByText(logs, "Activity configuration for account ${accountId} " + "contains conditional rule with empty array").size() == 1 @@ -1877,8 +1947,11 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { ] } - def "PBS amp call when privacy module contain invalid GPP string should remove EIDS fields in request"() { - given: "Default Generic BidRequest with EIDS fields field and account id" + def "PBS amp call when privacy module contain invalid GPP segment shouldn't remove EIDS fields in request and emit error log"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default Generic BidRequest with EIDS fields field and account id" def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) @@ -1886,7 +1959,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value - it.consentString = invalidGpp + it.consentString = INVALID_GPP_STRING it.consentType = GPP } @@ -1921,15 +1994,75 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { and: "Metrics processed across activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(ampStoredRequest, TRANSMIT_EIDS)] == 1 + assert metrics[ALERT_GENERAL.value] == 1 and: "Response should not contain any warnings" assert !response.ext.warnings - and: "Response should not contain any errors" - assert !response.ext.errors + and: "Response should contain consent_string errors" + assert response.ext.errors[PREBID].message == ["Amp request parameter consent_string has invalid format: $INVALID_GPP_STRING"] - where: - invalidGpp << [null, "", PBSUtils.randomString, INVALID_GPP_STRING] + "Response should contain error" + def logs = activityPbsService.getLogsByTime(startTime) + assert getLogsByText(logs, "UsNat privacy module creation failed: Unable to decode UsNatCoreSegment " + + "'${INVALID_GPP_SEGMENT}'. Activity: TRANSMIT_EIDS. Section: ${US_NAT_V1.value}. Gpp: $INVALID_GPP_STRING").size() == 1 + } + + def "PBS amp call when privacy module contain invalid GPP string shouldn't remove EIDS fields in request and emit warning in response"() { + given: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = getBidRequestWithPersonalData(accountId) + + and: "amp request with link to account" + def invalidGpp = PBSUtils.randomString + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + it.gppSid = US_NAT_V1.value + it.consentString = invalidGpp + it.consentType = GPP + } + + and: "Activities set for transmitEIDS with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration" + def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + when: "PBS processes amp request" + def response = activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + assert genericBidderRequest.user.eids[0].source == ampStoredRequest.user.eids[0].source + + and: "Generic bidder request should have data in EIDS fields" + assert genericBidderRequest.user.eids == ampStoredRequest.user.eids + + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(ampStoredRequest, TRANSMIT_EIDS)] == 1 + + and: "Should add a warning when in debug mode" + assert response.ext.warnings[PREBID]?.code == [999] + assert response.ext.warnings[PREBID]?.message == ["GPP string invalid: Unable to decode '$invalidGpp'"] + + and: "Response should contain consent_string errors" + assert response.ext.errors[PREBID].message == ["Amp request parameter consent_string has invalid format: $invalidGpp"] } def "PBS amp call when request have different gpp consent but match and rejecting should remove EIDS fields in request"() { @@ -2025,6 +2158,57 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { ] } + def "PBS amp call when regs.gpp empty in request should leave EIDS fields in request"() { + given: "Default Generic BidRequest with EIDS fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = getBidRequestWithPersonalData(accountId) + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + it.gppSid = US_NAT_V1.value + it.consentString = regsGpp + it.consentType = GPP + } + + and: "Activities set for transmitEIDS with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_EIDS, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration" + def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + def response = activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + assert genericBidderRequest.user.eids[0].source == ampStoredRequest.user.eids[0].source + + and: "Generic bidder request should have data in EIDS fields" + assert genericBidderRequest.user.eids == ampStoredRequest.user.eids + + and: "Response shouldn't contain errors" + assert !response.ext.errors + + and: "Response shouldn't contain warnings" + assert !response.ext.warnings + + where: + regsGpp << [null, ""] + } + def "PBS amp call when regs.gpp in request is allowing should leave EIDS fields in request"() { given: "Default Generic BidRequest with EIDS fields field and account id" def accountId = PBSUtils.randomNumber as String @@ -2057,14 +2241,23 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp request" - activityPbsService.sendAmpRequest(ampRequest) + def response = activityPbsService.sendAmpRequest(ampRequest) then: "Generic bidder request should have data in EIDS fields" def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) assert genericBidderRequest.user.eids[0].source == ampStoredRequest.user.eids[0].source + and: "Generic bidder request should have data in EIDS fields" + assert genericBidderRequest.user.eids == ampStoredRequest.user.eids + + and: "Response shouldn't contain warnings" + assert !response.ext.warnings + + and: "Response should contain consent_string errors" + assert response.ext.errors[PREBID].message == ["Amp request parameter consent_string has invalid format: $regsGpp"] + where: - regsGpp << ["", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] + regsGpp << [new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] } def "PBS amp call when privacy regulation have duplicate should leave EIDS fields in request and update alerts metrics"() { @@ -2258,8 +2451,11 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { new EqualityValueRule(PERSONAL_DATA_CONSENTS, NOTICE_NOT_PROVIDED)] } - def "PBS amp call when custom privacy regulation empty and normalize is disabled should respond with an error and update metric"() { - given: "Store bid request with link for account" + def "PBS amp call when custom privacy regulation empty and normalize is disabled should leave EIDS fields in request and emit error log"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Store bid request with link for account" def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) @@ -2298,17 +2494,25 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp requests" - activityPbsService.sendAmpRequest(ampRequest) + def response = activityPbsService.sendAmpRequest(ampRequest) - then: "Response should contain error" - def error = thrown(PrebidServerException) - assert error.statusCode == BAD_REQUEST.code() - assert error.responseBody == "Invalid account configuration: JsonLogic exception: " + - "objects must have exactly 1 key defined, found 0" + then: "Response should not contain any warnings" + assert !response.ext.warnings + + and: "Response should contain consent_string error" + assert response.ext.errors[PREBID].message == ["Amp request parameter consent_string has invalid format: $gppConsent"] and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() assert metrics[ALERT_GENERAL.getValue()] == 1 + + and: "Generic bidder request should have data in EIDS fields" + def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + assert genericBidderRequest.user.eids[0].source == ampStoredRequest.user.eids[0].source + + and: "Logs should contain error" + def logs = activityPbsService.getLogsByTime(startTime) + assert getLogsByText(logs, "USCustomLogic creation failed: objects must have exactly 1 key defined, found 0").size() == 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 e037940a660..df0e2bcf3ca 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 @@ -33,7 +33,6 @@ import org.prebid.server.functional.util.privacy.gpp.data.UsUtahSensitiveData import java.time.Instant -import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST import static io.netty.handler.codec.http.HttpResponseStatus.UNAUTHORIZED import static org.prebid.server.functional.model.config.DataActivity.CONSENT import static org.prebid.server.functional.model.config.DataActivity.NOTICE_NOT_PROVIDED @@ -247,7 +246,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { when: "PBS processes auction requests" activityPbsService.sendAuctionRequest(bidRequest) - then: "Response should contain error" + then: "Logs should contain error" def logs = activityPbsService.getLogsByTime(startTime) assert getLogsByText(logs, "Activity configuration for account ${accountId} " + "contains conditional rule with empty array").size() == 1 @@ -1109,12 +1108,15 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { ] } - def "PBS auction call when privacy module contain invalid GPP string should remove UFPD fields in request"() { - given: "Default Generic BidRequests with UFPD fields and account id" + def "PBS auction call when privacy module contain invalid GPP segment shouldn't remove UFPD fields in request and emit error log"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default Generic BidRequests with UFPD fields and account id" def accountId = PBSUtils.randomNumber as String def bidRequest = getBidRequestWithPersonalData(accountId).tap { regs.gppSid = [US_NAT_V1.intValue] - regs.gpp = invalidGpp + regs.gpp = INVALID_GPP_STRING } and: "Activities set for transmitUfpd with rejecting privacy regulation" @@ -1135,7 +1137,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { flushMetrics(activityPbsService) when: "PBS processes auction requests" - def response= activityPbsService.sendAuctionRequest(bidRequest) + def response = activityPbsService.sendAuctionRequest(bidRequest) then: "Generic bidder request should have data in UFPD fields" def bidderRequest = bidder.getBidderRequest(bidRequest.id) @@ -1170,9 +1172,77 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def metrics = activityPbsService.sendCollectedMetricsRequest() assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(bidRequest, TRANSMIT_UFPD)] == 1 assert metrics[ACCOUNT_PROCESSED_RULES_COUNT.getValue(bidRequest, TRANSMIT_UFPD)] == 1 + assert metrics[ALERT_GENERAL.value] == 1 - where: - invalidGpp << [null, "", PBSUtils.randomString, INVALID_GPP_STRING] + and: "Logs should contain error" + def logs = activityPbsService.getLogsByTime(startTime) + assert getLogsByText(logs, "UsNat privacy module creation failed: Unable to decode UsNatCoreSegment " + + "'${INVALID_GPP_SEGMENT}'. Activity: TRANSMIT_UFPD. Section: ${US_NAT_V1.value}. Gpp: $INVALID_GPP_STRING").size() == 1 + } + + def "PBS auction call when privacy module contain invalid GPP string shouldn't remove UFPD fields in request and emit warning in response"() { + given: "Default Generic BidRequests with UFPD fields and account id" + def accountId = PBSUtils.randomNumber as String + def invalidGpp = PBSUtils.randomString + def bidRequest = getBidRequestWithPersonalData(accountId).tap { + regs.gppSid = [US_NAT_V1.intValue] + regs.gpp = invalidGpp + } + + and: "Activities set for transmitUfpd with rejecting privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_UFPD, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration" + def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + when: "PBS processes auction requests" + def response = activityPbsService.sendAuctionRequest(bidRequest) + + then: "Generic bidder request should have data in UFPD fields" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + + verifyAll { + bidderRequest.device.didsha1 == bidRequest.device.didsha1 + bidderRequest.device.didmd5 == bidRequest.device.didmd5 + bidderRequest.device.dpidsha1 == bidRequest.device.dpidsha1 + bidderRequest.device.ifa == bidRequest.device.ifa + bidderRequest.device.macsha1 == bidRequest.device.macsha1 + bidderRequest.device.macmd5 == bidRequest.device.macmd5 + bidderRequest.device.dpidmd5 == bidRequest.device.dpidmd5 + bidderRequest.user.id == bidRequest.user.id + bidderRequest.user.buyeruid == bidRequest.user.buyeruid + bidderRequest.user.yob == bidRequest.user.yob + bidderRequest.user.gender == bidRequest.user.gender + bidderRequest.user.data == bidRequest.user.data + bidderRequest.user.geo.zip == bidRequest.user.geo.zip + bidderRequest.user.ext.data.buyeruid == bidRequest.user.ext.data.buyeruid + } + + and: "Generic bidder request should have data in EIDS fields" + assert bidderRequest.user.eids == bidRequest.user.eids + + and: "Should add a warning when in debug mode" + assert response.ext.warnings[PREBID]?.code == [999] + assert response.ext.warnings[PREBID]?.message == ["GPP string invalid: Unable to decode '$invalidGpp'"] + + and: "Response should not contain any errors" + assert !response.ext.errors + + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(bidRequest, TRANSMIT_UFPD)] == 1 + assert metrics[ACCOUNT_PROCESSED_RULES_COUNT.getValue(bidRequest, TRANSMIT_UFPD)] == 1 } def "PBS auction call when request have different gpp consent but match and rejecting should remove UFPD fields in request"() { @@ -1308,7 +1378,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { accountDao.save(account) when: "PBS processes auction requests" - activityPbsService.sendAuctionRequest(bidRequest) + def response = activityPbsService.sendAuctionRequest(bidRequest) then: "Generic bidder request should have data in UFPD fields" def bidderRequest = bidder.getBidderRequest(bidRequest.id) @@ -1334,8 +1404,14 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Generic bidder request should have data in EIDS fields" assert bidderRequest.user.eids == bidRequest.user.eids + and: "Response shouldn't contain errors" + assert !response.ext.errors + + and: "Response shouldn't contain warnings" + assert !response.ext.warnings + where: - regsGpp << ["", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] + regsGpp << [null, "", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] } def "PBS auction call when privacy regulation have duplicate should leave UFPD fields in request and update alerts metrics"() { @@ -1545,8 +1621,11 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { new EqualityValueRule(PERSONAL_DATA_CONSENTS, NOTICE_NOT_PROVIDED)] } - def "PBS auction call when custom privacy regulation empty and normalize is disabled should respond with an error and update metric"() { - given: "Generic BidRequest with gpp and account setup" + def "PBS auction call when custom privacy regulation empty and normalize is disabled should leave UFPD fields in request and emit error log"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Generic BidRequest with gpp and account setup" def gppConsent = new UsNatV1Consent.Builder().setGpc(true).build() def accountId = PBSUtils.randomNumber as String def bidRequest = getBidRequestWithPersonalData(accountId).tap { @@ -1577,16 +1656,45 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { accountDao.save(account) when: "PBS processes auction requests" - activityPbsService.sendAuctionRequest(bidRequest) + def response = activityPbsService.sendAuctionRequest(bidRequest) - then: "Response should contain error" - def error = thrown(PrebidServerException) - assert error.statusCode == BAD_REQUEST.code() - assert error.responseBody == "JsonLogic exception: objects must have exactly 1 key defined, found 0" + then: "Response should not contain any warnings" + assert !response.ext.warnings + + and: "Response should not contain any errors" + assert !response.ext.errors and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() assert metrics[ALERT_GENERAL.getValue()] == 1 + + and: "Generic bidder request should have data in UFPD fields" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + + and: "Generic bidder should be called due to positive allow in activities" + verifyAll { + bidderRequest.device.didsha1 == bidRequest.device.didsha1 + bidderRequest.device.didmd5 == bidRequest.device.didmd5 + bidderRequest.device.dpidsha1 == bidRequest.device.dpidsha1 + bidderRequest.device.ifa == bidRequest.device.ifa + bidderRequest.device.macsha1 == bidRequest.device.macsha1 + bidderRequest.device.macmd5 == bidRequest.device.macmd5 + bidderRequest.device.dpidmd5 == bidRequest.device.dpidmd5 + bidderRequest.user.id == bidRequest.user.id + bidderRequest.user.buyeruid == bidRequest.user.buyeruid + bidderRequest.user.yob == bidRequest.user.yob + bidderRequest.user.gender == bidRequest.user.gender + bidderRequest.user.data == bidRequest.user.data + bidderRequest.user.geo == bidRequest.user.geo + bidderRequest.user.ext.data.buyeruid == bidRequest.user.ext.data.buyeruid + } + + and: "Generic bidder request should have data in EIDS fields" + assert bidderRequest.user.eids == bidRequest.user.eids + + and: "Logs should contain error" + def logs = activityPbsService.getLogsByTime(startTime) + assert getLogsByText(logs, "USCustomLogic creation failed: objects must have exactly 1 key defined, found 0").size() == 1 } def "PBS auction call when custom privacy regulation with normalizing that match custom config should have empty UFPD fields"() { @@ -1903,7 +2011,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { when: "PBS processes amp request" activityPbsService.sendAmpRequest(ampRequest) - then: "Response should contain error" + then: "Logs should contain error" def logs = activityPbsService.getLogsByTime(startTime) assert getLogsByText(logs, "Activity configuration for account ${accountId} " + "contains conditional rule with empty array").size() == 1 @@ -2448,8 +2556,11 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { ] } - def "PBS amp call when privacy module contain invalid GPP string should remove UFPD fields in request"() { - given: "Default Generic BidRequest with UFPD fields field and account id" + def "PBS amp call when privacy module contain invalid GPP segment shouldn't remove UFPD fields in request and emit error log"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default Generic BidRequest with UFPD fields field and account id" def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) @@ -2457,7 +2568,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value - it.consentString = invalidGpp + it.consentString = INVALID_GPP_STRING it.consentType = GPP } @@ -2511,15 +2622,91 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Metrics processed across activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(ampStoredRequest, TRANSMIT_UFPD)] == 1 + assert metrics[ALERT_GENERAL.value] == 1 and: "Response should not contain any warnings" assert !response.ext.warnings - and: "Response should not contain any errors" - assert !response.ext.errors + and: "Response should contain consent_string errors" + assert response.ext.errors[PREBID].message == ["Amp request parameter consent_string has invalid format: $INVALID_GPP_STRING"] - where: - invalidGpp << [null, "", PBSUtils.randomString, INVALID_GPP_STRING] + "Response should contain error" + def logs = activityPbsService.getLogsByTime(startTime) + assert getLogsByText(logs, "UsNat privacy module creation failed: Unable to decode UsNatCoreSegment " + + "'${INVALID_GPP_SEGMENT}'. Activity: TRANSMIT_UFPD. Section: ${US_NAT_V1.value}. Gpp: $INVALID_GPP_STRING").size() == 1 + } + + def "PBS amp call when privacy module contain invalid GPP string shouldn't remove UFPD fields in request and emit warning in response"() { + given: "Default Generic BidRequest with UFPD fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = getBidRequestWithPersonalData(accountId) + + and: "amp request with link to account" + def invalidGpp = PBSUtils.randomString + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + it.gppSid = US_NAT_V1.value + it.consentString = invalidGpp + it.consentType = GPP + } + + and: "Activities set for transmitUfpd with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_UFPD, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration" + def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + and: "Flush metrics" + flushMetrics(activityPbsService) + + when: "PBS processes amp request" + def response = activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have data in UFPD fields" + def bidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + + verifyAll { + bidderRequest.device.didsha1 == ampStoredRequest.device.didsha1 + bidderRequest.device.didmd5 == ampStoredRequest.device.didmd5 + bidderRequest.device.dpidsha1 == ampStoredRequest.device.dpidsha1 + bidderRequest.device.ifa == ampStoredRequest.device.ifa + bidderRequest.device.macsha1 == ampStoredRequest.device.macsha1 + bidderRequest.device.macmd5 == ampStoredRequest.device.macmd5 + bidderRequest.device.dpidmd5 == ampStoredRequest.device.dpidmd5 + bidderRequest.user.id == ampStoredRequest.user.id + bidderRequest.user.buyeruid == ampStoredRequest.user.buyeruid + bidderRequest.user.yob == ampStoredRequest.user.yob + bidderRequest.user.gender == ampStoredRequest.user.gender + bidderRequest.user.data == ampStoredRequest.user.data + bidderRequest.user.geo == ampStoredRequest.user.geo + bidderRequest.user.ext.data.buyeruid == ampStoredRequest.user.ext.data.buyeruid + } + + and: "Generic bidder request should have data in EIDS fields" + assert bidderRequest.user.eids == ampStoredRequest.user.eids + + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(ampStoredRequest, TRANSMIT_UFPD)] == 1 + + and: "Should add a warning when in debug mode" + assert response.ext.warnings[PREBID]?.code == [999] + assert response.ext.warnings[PREBID]?.message == ["GPP string invalid: Unable to decode '$invalidGpp'"] + + and: "Response should contain consent_string errors" + assert response.ext.errors[PREBID].message == ["Amp request parameter consent_string has invalid format: $invalidGpp"] } def "PBS amp call when request have different gpp consent but match and rejecting should remove UFPD fields in request"() { @@ -2649,6 +2836,73 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { ] } + def "PBS amp call when regs.gpp empty in request should leave UFPD fields in request"() { + given: "Default Generic BidRequest with UFPD fields field and account id" + def accountId = PBSUtils.randomNumber as String + def ampStoredRequest = getBidRequestWithPersonalData(accountId) + + and: "amp request with link to account" + def ampRequest = AmpRequest.defaultAmpRequest.tap { + it.account = accountId + it.gppSid = US_NAT_V1.value + it.consentString = regsGpp + it.consentType = GPP + } + + and: "Activities set for transmitUfpd with allowing privacy regulation" + def rule = new ActivityRule().tap { + it.privacyRegulation = [IAB_US_GENERAL] + } + + def activities = AllowActivities.getDefaultAllowActivities(TRANSMIT_UFPD, Activity.getDefaultActivity([rule])) + + and: "Account gpp configuration" + def accountGppConfig = new AccountGppConfig(code: IAB_US_GENERAL, enabled: true) + + and: "Existed account with privacy regulation setup" + def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) + accountDao.save(account) + + and: "Stored request in DB" + def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) + storedRequestDao.save(storedRequest) + + when: "PBS processes amp request" + def response = activityPbsService.sendAmpRequest(ampRequest) + + then: "Generic bidder request should have data in UFPD fields" + def bidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + + verifyAll { + bidderRequest.device.didsha1 == ampStoredRequest.device.didsha1 + bidderRequest.device.didmd5 == ampStoredRequest.device.didmd5 + bidderRequest.device.dpidsha1 == ampStoredRequest.device.dpidsha1 + bidderRequest.device.ifa == ampStoredRequest.device.ifa + bidderRequest.device.macsha1 == ampStoredRequest.device.macsha1 + bidderRequest.device.macmd5 == ampStoredRequest.device.macmd5 + bidderRequest.device.dpidmd5 == ampStoredRequest.device.dpidmd5 + bidderRequest.user.id == ampStoredRequest.user.id + bidderRequest.user.buyeruid == ampStoredRequest.user.buyeruid + bidderRequest.user.yob == ampStoredRequest.user.yob + bidderRequest.user.gender == ampStoredRequest.user.gender + bidderRequest.user.data == ampStoredRequest.user.data + bidderRequest.user.geo == ampStoredRequest.user.geo + bidderRequest.user.ext.data.buyeruid == ampStoredRequest.user.ext.data.buyeruid + } + + and: "Generic bidder request should have data in EIDS fields" + assert bidderRequest.user.eids == ampStoredRequest.user.eids + + and: "Response shouldn't contain errors" + assert !response.ext.errors + + and: "Response shouldn't contain warnings" + assert !response.ext.warnings + + where: + regsGpp << [null, ""] + } + def "PBS amp call when regs.gpp in request is allowing should leave UFPD fields in request"() { given: "Default Generic BidRequest with UFPD fields field and account id" def accountId = PBSUtils.randomNumber as String @@ -2681,7 +2935,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp request" - activityPbsService.sendAmpRequest(ampRequest) + def response = activityPbsService.sendAmpRequest(ampRequest) then: "Generic bidder request should have data in UFPD fields" def bidderRequest = bidder.getBidderRequest(ampStoredRequest.id) @@ -2706,8 +2960,14 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Generic bidder request should have data in EIDS fields" assert bidderRequest.user.eids == ampStoredRequest.user.eids + and: "Response shouldn't contain warnings" + assert !response.ext.warnings + + and: "Response should contain consent_string errors" + assert response.ext.errors[PREBID].message == ["Amp request parameter consent_string has invalid format: $regsGpp"] + where: - regsGpp << ["", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] + regsGpp << [new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] } def "PBS amp call when privacy regulation have duplicate should leave UFPD fields in request and update alerts metrics"() { @@ -2953,8 +3213,11 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { new EqualityValueRule(PERSONAL_DATA_CONSENTS, NOTICE_NOT_PROVIDED)] } - def "PBS amp call when custom privacy regulation empty and normalize is disabled should respond with an error and update metric"() { - given: "Store bid request with link for account" + def "PBS amp call when custom privacy regulation empty and normalize is disabled should leave UFPD fields in request and emit error log"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Store bid request with link for account" def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) @@ -2993,17 +3256,40 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { storedRequestDao.save(storedRequest) when: "PBS processes amp requests" - activityPbsService.sendAmpRequest(ampRequest) + def response = activityPbsService.sendAmpRequest(ampRequest) - then: "Response should contain error" - def error = thrown(PrebidServerException) - assert error.statusCode == BAD_REQUEST.code() - assert error.responseBody == "Invalid account configuration: JsonLogic exception: " + - "objects must have exactly 1 key defined, found 0" + then: "Response should not contain any warnings" + assert !response.ext.warnings + + and: "Response should contain consent_string error" + assert response.ext.errors[PREBID].message == ["Amp request parameter consent_string has invalid format: $gppConsent"] and: "Metrics for disallowed activities should be updated" def metrics = activityPbsService.sendCollectedMetricsRequest() assert metrics[ALERT_GENERAL.getValue()] == 1 + + and: "Generic bidder request should have data in UFPD fields" + def bidderRequest = bidder.getBidderRequest(ampStoredRequest.id) + verifyAll { + bidderRequest.device.didsha1 == ampStoredRequest.device.didsha1 + bidderRequest.device.didmd5 == ampStoredRequest.device.didmd5 + bidderRequest.device.dpidsha1 == ampStoredRequest.device.dpidsha1 + bidderRequest.device.ifa == ampStoredRequest.device.ifa + bidderRequest.device.macsha1 == ampStoredRequest.device.macsha1 + bidderRequest.device.macmd5 == ampStoredRequest.device.macmd5 + bidderRequest.device.dpidmd5 == ampStoredRequest.device.dpidmd5 + bidderRequest.user.id == ampStoredRequest.user.id + bidderRequest.user.buyeruid == ampStoredRequest.user.buyeruid + bidderRequest.user.yob == ampStoredRequest.user.yob + bidderRequest.user.gender == ampStoredRequest.user.gender + bidderRequest.user.data == ampStoredRequest.user.data + bidderRequest.user.geo == ampStoredRequest.user.geo + bidderRequest.user.ext.data.buyeruid == ampStoredRequest.user.ext.data.buyeruid + } + + and: "Logs should contain error" + def logs = activityPbsService.getLogsByTime(startTime) + assert getLogsByText(logs, "USCustomLogic creation failed: objects must have exactly 1 key defined, found 0").size() == 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/PrivacyBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/PrivacyBaseSpec.groovy index 04765af3647..e16dc69dd62 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/PrivacyBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/PrivacyBaseSpec.groovy @@ -95,7 +95,8 @@ abstract class PrivacyBaseSpec extends BaseSpec { private static final Map GDPR_EEA_COUNTRY = ["gdpr.eea-countries": "$BULGARIA.ISOAlpha2, SK, VK" as String] protected static final String VENDOR_LIST_PATH = "/app/prebid-server/data/vendorlist-v{VendorVersion}/{VendorVersion}.json" - protected static final String INVALID_GPP_STRING = "DBABLA~${PBSUtils.getRandomString(7)}.YA" + protected static final String INVALID_GPP_SEGMENT = PBSUtils.getRandomString(7) + protected static final String INVALID_GPP_STRING = "DBABLA~${INVALID_GPP_SEGMENT}.YA" protected static final String VALID_VALUE_FOR_GPC_HEADER = "1" protected static final GppConsent SIMPLE_GPC_DISALLOW_LOGIC = new UsNatV1Consent.Builder().setGpc(true).build() protected static final VendorList vendorListResponse = new VendorList(networkServiceContainer) From 5cc9ff573c5e3f3fd7425a1c532f2357b92415f7 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Mon, 19 May 2025 15:29:06 +0300 Subject: [PATCH 3/3] update after review --- .../privacy/GppSyncUserActivitiesSpec.groovy | 20 ++++++ .../GppTransmitEidsActivitiesSpec.groovy | 68 +++++++++++-------- .../GppTransmitUfpdActivitiesSpec.groovy | 62 +++++++++++------ 3 files changed, 103 insertions(+), 47 deletions(-) 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 0c7c9baa26b..89a3273011a 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 @@ -669,6 +669,9 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) accountDao.save(account) + and: "Flush metrics" + flushMetrics(activityPbsService) + when: "PBS processes cookie sync request" def response = activityPbsService.sendCookieSyncRequest(cookieSyncRequest) @@ -678,6 +681,13 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { and: "Response shouldn't contain warnings" assert !response.warnings + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(cookieSyncRequest, SYNC_USER)] == 1 + + and: "General alert metric shouldn't be updated" + !metrics[ALERT_GENERAL.getValue()] + where: regsGpp << [null, "", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] } @@ -1621,6 +1631,9 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) accountDao.save(account) + and: "Flush metrics" + flushMetrics(activityPbsService) + when: "PBS processes cookie sync request" def response = activityPbsService.sendSetUidRequest(setuidRequest, uidsCookie) @@ -1628,6 +1641,13 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { assert response.uidsCookie assert response.responseBody + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(setuidRequest, SYNC_USER)] == 1 + + and: "General alert metric shouldn't be updated" + !metrics[ALERT_GENERAL.getValue()] + where: regsGpp << [null, "", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] } 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 ab4bd92a93e..76cdff173dc 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 @@ -1038,6 +1038,9 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) accountDao.save(account) + and: "Flush metrics" + flushMetrics(activityPbsService) + when: "PBS processes auction requests" def response = activityPbsService.sendAuctionRequest(bidRequest) @@ -1051,6 +1054,14 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { and: "Response shouldn't contain warnings" assert !response.ext.warnings + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(bidRequest, TRANSMIT_EIDS)] == 1 + assert metrics[ACCOUNT_PROCESSED_RULES_COUNT.getValue(bidRequest, TRANSMIT_EIDS)] == 1 + + and: "General alert metric shouldn't be updated" + !metrics[ALERT_GENERAL.getValue()] + where: regsGpp << [null, "", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] } @@ -1381,7 +1392,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId } @@ -1417,7 +1428,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId } @@ -1458,7 +1469,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId } @@ -1495,7 +1506,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId } @@ -1533,7 +1544,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId } @@ -1567,7 +1578,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId } @@ -1606,7 +1617,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { it.regs.ext = new RegsExt(gpc: null) } - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId } @@ -1652,7 +1663,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId } @@ -1694,7 +1705,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -1737,7 +1748,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -1902,7 +1913,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -1955,7 +1966,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -2013,7 +2024,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def invalidGpp = PBSUtils.randomString def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId @@ -2048,9 +2059,6 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { then: "Generic bidder request should have data in EIDS fields" def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) - assert genericBidderRequest.user.eids[0].source == ampStoredRequest.user.eids[0].source - - and: "Generic bidder request should have data in EIDS fields" assert genericBidderRequest.user.eids == ampStoredRequest.user.eids and: "Metrics processed across activities should be updated" @@ -2070,7 +2078,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = gppSid.value @@ -2121,7 +2129,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -2163,7 +2171,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -2189,14 +2197,14 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) storedRequestDao.save(storedRequest) + and: "Flush metrics" + flushMetrics(activityPbsService) + when: "PBS processes amp request" def response = activityPbsService.sendAmpRequest(ampRequest) then: "Generic bidder request should have data in EIDS fields" def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) - assert genericBidderRequest.user.eids[0].source == ampStoredRequest.user.eids[0].source - - and: "Generic bidder request should have data in EIDS fields" assert genericBidderRequest.user.eids == ampStoredRequest.user.eids and: "Response shouldn't contain errors" @@ -2205,6 +2213,13 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { and: "Response shouldn't contain warnings" assert !response.ext.warnings + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(ampStoredRequest, TRANSMIT_EIDS)] == 1 + + and: "General alert metric shouldn't be updated" + !metrics[ALERT_GENERAL.getValue()] + where: regsGpp << [null, ""] } @@ -2214,7 +2229,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -2245,9 +2260,6 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { then: "Generic bidder request should have data in EIDS fields" def genericBidderRequest = bidder.getBidderRequest(ampStoredRequest.id) - assert genericBidderRequest.user.eids[0].source == ampStoredRequest.user.eids[0].source - - and: "Generic bidder request should have data in EIDS fields" assert genericBidderRequest.user.eids == ampStoredRequest.user.eids and: "Response shouldn't contain warnings" @@ -2265,7 +2277,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -2311,7 +2323,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value 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 df0e2bcf3ca..9608e479a7c 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 @@ -1377,6 +1377,9 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) accountDao.save(account) + and: "Flush metrics" + flushMetrics(activityPbsService) + when: "PBS processes auction requests" def response = activityPbsService.sendAuctionRequest(bidRequest) @@ -1410,6 +1413,14 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Response shouldn't contain warnings" assert !response.ext.warnings + and: "Metrics for disallowed activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(bidRequest, TRANSMIT_UFPD)] == 1 + assert metrics[ACCOUNT_PROCESSED_RULES_COUNT.getValue(bidRequest, TRANSMIT_UFPD)] == 1 + + and: "General alert metric shouldn't be updated" + !metrics[ALERT_GENERAL.getValue()] + where: regsGpp << [null, "", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] } @@ -1828,7 +1839,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId } @@ -1883,7 +1894,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId } @@ -1939,7 +1950,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId } @@ -1991,7 +2002,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId } @@ -2029,7 +2040,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId } @@ -2082,7 +2093,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId } @@ -2136,7 +2147,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { it.regs.ext = new RegsExt(gpc: null) } - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId } @@ -2197,7 +2208,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId } @@ -2258,7 +2269,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -2316,7 +2327,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -2496,7 +2507,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -2564,7 +2575,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -2641,7 +2652,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def invalidGpp = PBSUtils.randomString def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId @@ -2714,7 +2725,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = gppSid.value @@ -2780,7 +2791,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -2841,7 +2852,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -2867,6 +2878,9 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def storedRequest = StoredRequest.getStoredRequest(ampRequest, ampStoredRequest) storedRequestDao.save(storedRequest) + and: "Flush metrics" + flushMetrics(activityPbsService) + when: "PBS processes amp request" def response = activityPbsService.sendAmpRequest(ampRequest) @@ -2899,6 +2913,13 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { and: "Response shouldn't contain warnings" assert !response.ext.warnings + and: "Metrics processed across activities should be updated" + def metrics = activityPbsService.sendCollectedMetricsRequest() + assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(ampStoredRequest, TRANSMIT_UFPD)] == 1 + + and: "General alert metric shouldn't be updated" + !metrics[ALERT_GENERAL.getValue()] + where: regsGpp << [null, ""] } @@ -2908,7 +2929,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -2975,7 +2996,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -3040,7 +3061,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def accountId = PBSUtils.randomNumber as String def ampStoredRequest = getBidRequestWithPersonalData(accountId) - and: "amp request with link to account" + and: "Default amp request with link to account" def ampRequest = AmpRequest.defaultAmpRequest.tap { it.account = accountId it.gppSid = US_NAT_V1.value @@ -3287,6 +3308,9 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { bidderRequest.user.ext.data.buyeruid == ampStoredRequest.user.ext.data.buyeruid } + and: "Generic bidder request should have data in EIDS fields" + assert bidderRequest.user.eids == ampStoredRequest.user.eids + and: "Logs should contain error" def logs = activityPbsService.getLogsByTime(startTime) assert getLogsByText(logs, "USCustomLogic creation failed: objects must have exactly 1 key defined, found 0").size() == 1