From 089ed0380f2c0686efad4ba0fbcf8313aa36a942 Mon Sep 17 00:00:00 2001 From: Danylo Date: Thu, 15 May 2025 11:59:20 +0200 Subject: [PATCH 1/4] Fix checkstyle and move test to a proper package --- .../ActivityInfrastructureCreator.java | 18 +++++++++++- .../USCustomLogicModuleCreator.java | 29 +++++++++++++++---- .../privacy/usnat/USNatModuleCreator.java | 27 +++++++++++++++-- .../rule/PrivacyModulesRuleCreator.java | 23 +++++++++++++-- .../ActivityInfrastructureConfiguration.java | 28 +++++++++++++----- .../USCustomLogicModuleCreatorTest.java | 2 +- .../privacy/usnat/USNatModuleCreatorTest.java | 6 +++- .../rule/PrivacyModulesRuleCreatorTest.java | 9 ++++-- 8 files changed, 119 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/prebid/server/activity/infrastructure/creator/ActivityInfrastructureCreator.java b/src/main/java/org/prebid/server/activity/infrastructure/creator/ActivityInfrastructureCreator.java index ae139798dcf..dff0f808435 100644 --- a/src/main/java/org/prebid/server/activity/infrastructure/creator/ActivityInfrastructureCreator.java +++ b/src/main/java/org/prebid/server/activity/infrastructure/creator/ActivityInfrastructureCreator.java @@ -23,6 +23,7 @@ import org.prebid.server.settings.model.Purposes; import org.prebid.server.settings.model.activity.AccountActivityConfiguration; import org.prebid.server.settings.model.activity.privacy.AccountPrivacyModuleConfig; +import org.prebid.server.settings.model.activity.rule.AccountActivityRuleConfig; import java.util.Arrays; import java.util.Collections; @@ -171,7 +172,8 @@ private ActivityController from(Activity activity, final boolean allow = allowFromConfig(activityConfiguration.getAllow()); final List rules = ListUtils.emptyIfNull(activityConfiguration.getRules()).stream() .filter(Objects::nonNull) - .map(ruleConfiguration -> activityRuleFactory.from(ruleConfiguration, creationContext)) + .map(ruleConfiguration -> createRule(ruleConfiguration, creationContext)) + .filter(Objects::nonNull) .toList(); return ActivityController.of(allow, rules, debug); @@ -181,6 +183,20 @@ private static boolean allowFromConfig(Boolean configValue) { return configValue != null ? configValue : ActivityInfrastructure.ALLOW_ACTIVITY_BY_DEFAULT; } + private Rule createRule(AccountActivityRuleConfig ruleConfiguration, + ActivityControllerCreationContext creationContext) { + + try { + return activityRuleFactory.from(ruleConfiguration, creationContext); + } catch (Exception e) { + logger.error("ActivityInfrastructure rule creation failed: %s. Configuration: %s" + .formatted(e.getMessage(), ruleConfiguration)); + metrics.updateAlertsMetrics(MetricName.general); + + return null; + } + } + private static Supplier> enumMapFactory() { return () -> new EnumMap<>(Activity.class); } diff --git a/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/uscustomlogic/USCustomLogicModuleCreator.java b/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/uscustomlogic/USCustomLogicModuleCreator.java index 026e856a585..5760c70db91 100644 --- a/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/uscustomlogic/USCustomLogicModuleCreator.java +++ b/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/uscustomlogic/USCustomLogicModuleCreator.java @@ -17,6 +17,9 @@ import org.prebid.server.exception.InvalidAccountConfigException; import org.prebid.server.json.DecodeException; import org.prebid.server.json.JsonLogic; +import org.prebid.server.log.ConditionalLogger; +import org.prebid.server.log.Logger; +import org.prebid.server.log.LoggerFactory; import org.prebid.server.metric.MetricName; import org.prebid.server.metric.Metrics; import org.prebid.server.settings.SettingsCache; @@ -34,6 +37,9 @@ public class USCustomLogicModuleCreator implements PrivacyModuleCreator { + private static final Logger logger = LoggerFactory.getLogger(USCustomLogicModuleCreator.class); + private static final ConditionalLogger conditionalLogger = new ConditionalLogger(logger); + private static final Set ALLOWED_SECTIONS_IDS = PrivacySection.US_PRIVACY_SECTIONS.stream() .map(PrivacySection::sectionId) @@ -43,16 +49,19 @@ public class USCustomLogicModuleCreator implements PrivacyModuleCreator { private final JsonLogic jsonLogic; private final Map jsonLogicNodesCache; private final Metrics metrics; + private final double samplingRate; public USCustomLogicModuleCreator(USCustomLogicGppReaderFactory gppReaderFactory, JsonLogic jsonLogic, Integer cacheTtl, Integer cacheSize, - Metrics metrics) { + Metrics metrics, + double samplingRate) { this.gppReaderFactory = Objects.requireNonNull(gppReaderFactory); this.jsonLogic = Objects.requireNonNull(jsonLogic); this.metrics = Objects.requireNonNull(metrics); + this.samplingRate = samplingRate; jsonLogicNodesCache = cacheTtl != null && cacheSize != null ? SettingsCache.createCache(cacheTtl, cacheSize, 0) @@ -76,6 +85,7 @@ public PrivacyModule from(PrivacyModuleCreationContext creationContext) { ? SetUtils.emptyIfNull(scope.getSectionsIds()).stream() .filter(sectionId -> shouldApplyPrivacy(sectionId, moduleConfig)) .map(sectionId -> forConfig(sectionId, normalizeSection, scope.getGppModel(), jsonLogicConfig)) + .filter(Objects::nonNull) .toList() : Collections.emptyList(); @@ -123,10 +133,19 @@ private PrivacyModule forConfig(int sectionId, GppModel gppModel, ObjectNode jsonLogicConfig) { - return new USCustomLogicModule( - jsonLogic, - jsonLogicNode(jsonLogicConfig), - USCustomLogicDataSupplier.of(gppReaderFactory.forSection(sectionId, normalizeSection, gppModel))); + try { + return new USCustomLogicModule( + jsonLogic, + jsonLogicNode(jsonLogicConfig), + USCustomLogicDataSupplier.of(gppReaderFactory.forSection(sectionId, normalizeSection, gppModel))); + } catch (Exception e) { + conditionalLogger.error( + "USCustomLogic creation failed: %s. Config: %s".formatted(e.getMessage(), jsonLogicConfig), + samplingRate); + metrics.updateAlertsMetrics(MetricName.general); + + return null; + } } private JsonLogicNode jsonLogicNode(ObjectNode jsonLogicConfig) { diff --git a/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat/USNatModuleCreator.java b/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat/USNatModuleCreator.java index 817cbff36cc..bd42647e086 100644 --- a/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat/USNatModuleCreator.java +++ b/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat/USNatModuleCreator.java @@ -11,6 +11,11 @@ import org.prebid.server.activity.infrastructure.privacy.PrivacySection; import org.prebid.server.activity.infrastructure.privacy.usnat.USNatModule; import org.prebid.server.auction.gpp.model.GppContext; +import org.prebid.server.log.ConditionalLogger; +import org.prebid.server.log.Logger; +import org.prebid.server.log.LoggerFactory; +import org.prebid.server.metric.MetricName; +import org.prebid.server.metric.Metrics; import org.prebid.server.settings.model.activity.privacy.AccountUSNatModuleConfig; import java.util.List; @@ -20,15 +25,22 @@ public class USNatModuleCreator implements PrivacyModuleCreator { + private static final Logger logger = LoggerFactory.getLogger(USNatModuleCreator.class); + private static final ConditionalLogger conditionalLogger = new ConditionalLogger(logger); + private static final Set ALLOWED_SECTIONS_IDS = PrivacySection.US_PRIVACY_SECTIONS.stream() .map(PrivacySection::sectionId) .collect(Collectors.toSet()); private final USNatGppReaderFactory gppReaderFactory; + private final Metrics metrics; + private final double samplingRate; - public USNatModuleCreator(USNatGppReaderFactory gppReaderFactory) { + public USNatModuleCreator(USNatGppReaderFactory gppReaderFactory, Metrics metrics, double samplingRate) { this.gppReaderFactory = Objects.requireNonNull(gppReaderFactory); + this.metrics = Objects.requireNonNull(metrics); + this.samplingRate = samplingRate; } @Override @@ -48,6 +60,7 @@ public PrivacyModule from(PrivacyModuleCreationContext creationContext) { sectionId, scope.getGppModel(), moduleConfig.getConfig())) + .filter(Objects::nonNull) .toList(); return new AndPrivacyModules(innerPrivacyModules); @@ -70,6 +83,16 @@ private PrivacyModule forSection(Activity activity, GppModel gppModel, AccountUSNatModuleConfig.Config config) { - return new USNatModule(activity, gppReaderFactory.forSection(sectionId, gppModel), config); + try { + return new USNatModule(activity, gppReaderFactory.forSection(sectionId, gppModel), config); + } catch (Exception e) { + conditionalLogger.error( + "UsNat privacy module creation failed: %s. Activity: %s. Section: %s. Gpp: %s." + .formatted(e.getMessage(), activity, sectionId, gppModel.encode()), + samplingRate); + metrics.updateAlertsMetrics(MetricName.general); + + return null; + } } } diff --git a/src/main/java/org/prebid/server/activity/infrastructure/creator/rule/PrivacyModulesRuleCreator.java b/src/main/java/org/prebid/server/activity/infrastructure/creator/rule/PrivacyModulesRuleCreator.java index cf25e716c5d..8cf5bfdc0a2 100644 --- a/src/main/java/org/prebid/server/activity/infrastructure/creator/rule/PrivacyModulesRuleCreator.java +++ b/src/main/java/org/prebid/server/activity/infrastructure/creator/rule/PrivacyModulesRuleCreator.java @@ -10,6 +10,10 @@ import org.prebid.server.activity.infrastructure.privacy.SkippedPrivacyModule; import org.prebid.server.activity.infrastructure.rule.AndRule; import org.prebid.server.activity.infrastructure.rule.Rule; +import org.prebid.server.log.Logger; +import org.prebid.server.log.LoggerFactory; +import org.prebid.server.metric.MetricName; +import org.prebid.server.metric.Metrics; import org.prebid.server.settings.model.activity.privacy.AccountPrivacyModuleConfig; import org.prebid.server.settings.model.activity.rule.AccountActivityPrivacyModulesRuleConfig; @@ -23,15 +27,19 @@ public class PrivacyModulesRuleCreator extends AbstractRuleCreator { + private static final Logger logger = LoggerFactory.getLogger(PrivacyModulesRuleCreator.class); + private static final String WILDCARD = "*"; private final Map privacyModulesCreators; + private final Metrics metrics; - public PrivacyModulesRuleCreator(List privacyModulesCreators) { + public PrivacyModulesRuleCreator(List privacyModulesCreators, Metrics metrics) { super(AccountActivityPrivacyModulesRuleConfig.class); this.privacyModulesCreators = Objects.requireNonNull(privacyModulesCreators).stream() .collect(Collectors.toMap(PrivacyModuleCreator::qualifier, UnaryOperator.identity())); + this.metrics = Objects.requireNonNull(metrics); } @Override @@ -46,6 +54,7 @@ protected Rule fromConfiguration(AccountActivityPrivacyModulesRuleConfig ruleCon .filter(qualifier -> !creationContext.isUsed(qualifier)) .peek(creationContext::use) .map(qualifier -> createPrivacyModule(qualifier, creationContext)) + .filter(Objects::nonNull) .toList(); return new AndRule(privacyModules); @@ -87,8 +96,16 @@ private PrivacyModule createPrivacyModule(PrivacyModuleQualifier privacyModuleQu return new SkippedPrivacyModule(privacyModuleQualifier); } - return privacyModulesCreators.get(privacyModuleQualifier) - .from(creationContext(privacyModuleQualifier, creationContext)); + try { + return privacyModulesCreators.get(privacyModuleQualifier) + .from(creationContext(privacyModuleQualifier, creationContext)); + } catch (Exception e) { + logger.error("PrivacyModule %s creation failed: %s." + .formatted(privacyModuleQualifier, e.getMessage())); + metrics.updateAlertsMetrics(MetricName.general); + + return null; + } } private static PrivacyModuleCreationContext creationContext(PrivacyModuleQualifier privacyModuleQualifier, diff --git a/src/main/java/org/prebid/server/spring/config/ActivityInfrastructureConfiguration.java b/src/main/java/org/prebid/server/spring/config/ActivityInfrastructureConfiguration.java index 486951c276f..d6561b8fe32 100644 --- a/src/main/java/org/prebid/server/spring/config/ActivityInfrastructureConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/ActivityInfrastructureConfiguration.java @@ -35,8 +35,11 @@ USNatGppReaderFactory usNatGppReaderFactory() { } @Bean - USNatModuleCreator usNatModuleCreator(USNatGppReaderFactory gppReaderFactory) { - return new USNatModuleCreator(gppReaderFactory); + USNatModuleCreator usNatModuleCreator(USNatGppReaderFactory gppReaderFactory, + Metrics metrics, + @Value("${logging.sampling-rate:0.01}") double logSamplingRate) { + + return new USNatModuleCreator(gppReaderFactory, metrics, logSamplingRate); } } @@ -54,9 +57,16 @@ USCustomLogicModuleCreator usCustomLogicModuleCreator( JsonLogic jsonLogic, @Value("${settings.in-memory-cache.ttl-seconds:#{null}}") Integer ttlSeconds, @Value("${settings.in-memory-cache.cache-size:#{null}}") Integer cacheSize, - Metrics metrics) { - - return new USCustomLogicModuleCreator(gppReaderFactory, jsonLogic, ttlSeconds, cacheSize, metrics); + Metrics metrics, + @Value("${logging.sampling-rate:0.01}") double logSamplingRate) { + + return new USCustomLogicModuleCreator( + gppReaderFactory, + jsonLogic, + ttlSeconds, + cacheSize, + metrics, + logSamplingRate); } } } @@ -65,13 +75,15 @@ USCustomLogicModuleCreator usCustomLogicModuleCreator( static class RuleCreatorConfiguration { @Bean - ConditionsRuleCreator geoRuleCreator() { + ConditionsRuleCreator conditionsRuleCreator() { return new ConditionsRuleCreator(); } @Bean - PrivacyModulesRuleCreator privacyModulesRuleCreator(List privacyModuleCreators) { - return new PrivacyModulesRuleCreator(privacyModuleCreators); + PrivacyModulesRuleCreator privacyModulesRuleCreator(List privacyModuleCreators, + Metrics metrics) { + + return new PrivacyModulesRuleCreator(privacyModuleCreators, metrics); } } diff --git a/src/test/java/org/prebid/server/activity/infrastructure/creator/privacy/uscustomlogic/USCustomLogicModuleCreatorTest.java b/src/test/java/org/prebid/server/activity/infrastructure/creator/privacy/uscustomlogic/USCustomLogicModuleCreatorTest.java index 3d10dd5b9a4..0a52f8ae86c 100644 --- a/src/test/java/org/prebid/server/activity/infrastructure/creator/privacy/uscustomlogic/USCustomLogicModuleCreatorTest.java +++ b/src/test/java/org/prebid/server/activity/infrastructure/creator/privacy/uscustomlogic/USCustomLogicModuleCreatorTest.java @@ -63,7 +63,7 @@ public void setUp() { .willReturn(new USNationalGppReader(null)); given(jsonLogic.parse(any())).willReturn(JsonLogicBoolean.TRUE); - target = new USCustomLogicModuleCreator(gppReaderFactory, jsonLogic, null, null, metrics); + target = new USCustomLogicModuleCreator(gppReaderFactory, jsonLogic, null, null, metrics, 0); } @Test diff --git a/src/test/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat/USNatModuleCreatorTest.java b/src/test/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat/USNatModuleCreatorTest.java index f1b098c6562..16865ffa5f2 100644 --- a/src/test/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat/USNatModuleCreatorTest.java +++ b/src/test/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat/USNatModuleCreatorTest.java @@ -12,6 +12,7 @@ import org.prebid.server.activity.infrastructure.privacy.usnat.reader.USNationalGppReader; import org.prebid.server.activity.infrastructure.rule.Rule; import org.prebid.server.auction.gpp.model.GppContextCreator; +import org.prebid.server.metric.Metrics; import org.prebid.server.settings.model.activity.privacy.AccountUSNatModuleConfig; import java.util.List; @@ -35,13 +36,16 @@ public class USNatModuleCreatorTest { @Mock(strictness = LENIENT) private USNatGppReaderFactory gppReaderFactory; + @Mock(strictness = LENIENT) + private Metrics metrics; + private USNatModuleCreator target; @BeforeEach public void setUp() { given(gppReaderFactory.forSection(anyInt(), any())).willReturn(new USNationalGppReader(null)); - target = new USNatModuleCreator(gppReaderFactory); + target = new USNatModuleCreator(gppReaderFactory, metrics, 0); } @Test diff --git a/src/test/java/org/prebid/server/activity/infrastructure/creator/rule/PrivacyModulesRuleCreatorTest.java b/src/test/java/org/prebid/server/activity/infrastructure/creator/rule/PrivacyModulesRuleCreatorTest.java index 095c52ed14e..f3f92de08f0 100644 --- a/src/test/java/org/prebid/server/activity/infrastructure/creator/rule/PrivacyModulesRuleCreatorTest.java +++ b/src/test/java/org/prebid/server/activity/infrastructure/creator/rule/PrivacyModulesRuleCreatorTest.java @@ -11,6 +11,7 @@ import org.prebid.server.activity.infrastructure.privacy.PrivacyModuleQualifier; import org.prebid.server.activity.infrastructure.privacy.TestPrivacyModule; import org.prebid.server.activity.infrastructure.rule.Rule; +import org.prebid.server.metric.Metrics; import org.prebid.server.settings.model.activity.privacy.AccountPrivacyModuleConfig; import org.prebid.server.settings.model.activity.privacy.AccountUSNatModuleConfig; import org.prebid.server.settings.model.activity.rule.AccountActivityPrivacyModulesRuleConfig; @@ -24,6 +25,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; @ExtendWith(MockitoExtension.class) public class PrivacyModulesRuleCreatorTest { @@ -31,13 +33,16 @@ public class PrivacyModulesRuleCreatorTest { @Mock private PrivacyModuleCreator privacyModuleCreator; + @Mock(strictness = LENIENT) + private Metrics metrics; + private PrivacyModulesRuleCreator target; @BeforeEach public void setUp() { given(privacyModuleCreator.qualifier()).willReturn(PrivacyModuleQualifier.US_NAT); - target = new PrivacyModulesRuleCreator(singletonList(privacyModuleCreator)); + target = new PrivacyModulesRuleCreator(singletonList(privacyModuleCreator), metrics); } @Test @@ -177,7 +182,7 @@ public void fromShouldDisableSkippedPrivacyModule() { @Test public void fromShouldSkipPrivacyModuleWithoutCreator() { // given - target = new PrivacyModulesRuleCreator(emptyList()); + target = new PrivacyModulesRuleCreator(emptyList(), metrics); final AccountActivityPrivacyModulesRuleConfig config = AccountActivityPrivacyModulesRuleConfig.of( singletonList(PrivacyModuleQualifier.US_NAT.moduleName())); From 85a05f351353ad827095720aa0572eaeb4b77523 Mon Sep 17 00:00:00 2001 From: Danylo Date: Thu, 15 May 2025 13:09:50 +0200 Subject: [PATCH 2/4] Add units --- .../USCustomLogicModuleCreator.java | 15 ++------ .../privacy/usnat/USNatModuleCreator.java | 4 +-- .../rule/PrivacyModulesRuleCreator.java | 10 +++--- .../ActivityInfrastructureCreatorTest.java | 35 +++++++++++++++++++ .../USCustomLogicModuleCreatorTest.java | 9 ++--- .../privacy/usnat/USNatModuleCreatorTest.java | 28 ++++++++++++++- .../rule/PrivacyModulesRuleCreatorTest.java | 20 +++++++++++ 7 files changed, 97 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/uscustomlogic/USCustomLogicModuleCreator.java b/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/uscustomlogic/USCustomLogicModuleCreator.java index 5760c70db91..357eb916b5a 100644 --- a/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/uscustomlogic/USCustomLogicModuleCreator.java +++ b/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/uscustomlogic/USCustomLogicModuleCreator.java @@ -14,8 +14,6 @@ import org.prebid.server.activity.infrastructure.privacy.uscustomlogic.USCustomLogicDataSupplier; import org.prebid.server.activity.infrastructure.privacy.uscustomlogic.USCustomLogicModule; import org.prebid.server.auction.gpp.model.GppContext; -import org.prebid.server.exception.InvalidAccountConfigException; -import org.prebid.server.json.DecodeException; import org.prebid.server.json.JsonLogic; import org.prebid.server.log.ConditionalLogger; import org.prebid.server.log.Logger; @@ -151,16 +149,7 @@ private PrivacyModule forConfig(int sectionId, private JsonLogicNode jsonLogicNode(ObjectNode jsonLogicConfig) { final String jsonAsString = jsonLogicConfig.toString(); return jsonLogicNodesCache != null - ? jsonLogicNodesCache.computeIfAbsent(jsonAsString, this::parseJsonLogicNode) - : parseJsonLogicNode(jsonAsString); - } - - private JsonLogicNode parseJsonLogicNode(String jsonLogicConfig) { - try { - return jsonLogic.parse(jsonLogicConfig); - } catch (DecodeException e) { - metrics.updateAlertsMetrics(MetricName.general); - throw new InvalidAccountConfigException("JsonLogic exception: " + e.getMessage()); - } + ? jsonLogicNodesCache.computeIfAbsent(jsonAsString, jsonLogic::parse) + : jsonLogic.parse(jsonAsString); } } diff --git a/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat/USNatModuleCreator.java b/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat/USNatModuleCreator.java index bd42647e086..f7d7f0fbfcd 100644 --- a/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat/USNatModuleCreator.java +++ b/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat/USNatModuleCreator.java @@ -87,8 +87,8 @@ private PrivacyModule forSection(Activity activity, return new USNatModule(activity, gppReaderFactory.forSection(sectionId, gppModel), config); } catch (Exception e) { conditionalLogger.error( - "UsNat privacy module creation failed: %s. Activity: %s. Section: %s. Gpp: %s." - .formatted(e.getMessage(), activity, sectionId, gppModel.encode()), + "UsNat privacy module creation failed: %s. Activity: %s. Section: %s. Gpp: %s.".formatted( + e.getMessage(), activity, sectionId, gppModel != null ? gppModel.encode() : null), samplingRate); metrics.updateAlertsMetrics(MetricName.general); diff --git a/src/main/java/org/prebid/server/activity/infrastructure/creator/rule/PrivacyModulesRuleCreator.java b/src/main/java/org/prebid/server/activity/infrastructure/creator/rule/PrivacyModulesRuleCreator.java index 8cf5bfdc0a2..80c13e322d9 100644 --- a/src/main/java/org/prebid/server/activity/infrastructure/creator/rule/PrivacyModulesRuleCreator.java +++ b/src/main/java/org/prebid/server/activity/infrastructure/creator/rule/PrivacyModulesRuleCreator.java @@ -52,7 +52,6 @@ protected Rule fromConfiguration(AccountActivityPrivacyModulesRuleConfig ruleCon .map(configuredModuleName -> mapToModulesQualifiers(configuredModuleName, creationContext)) .flatMap(Collection::stream) .filter(qualifier -> !creationContext.isUsed(qualifier)) - .peek(creationContext::use) .map(qualifier -> createPrivacyModule(qualifier, creationContext)) .filter(Objects::nonNull) .toList(); @@ -93,15 +92,18 @@ private PrivacyModule createPrivacyModule(PrivacyModuleQualifier privacyModuleQu ActivityControllerCreationContext creationContext) { if (creationContext.getSkipPrivacyModules().contains(privacyModuleQualifier)) { + creationContext.use(privacyModuleQualifier); return new SkippedPrivacyModule(privacyModuleQualifier); } try { - return privacyModulesCreators.get(privacyModuleQualifier) + final PrivacyModule privacyModule = privacyModulesCreators.get(privacyModuleQualifier) .from(creationContext(privacyModuleQualifier, creationContext)); + creationContext.use(privacyModuleQualifier); + + return privacyModule; } catch (Exception e) { - logger.error("PrivacyModule %s creation failed: %s." - .formatted(privacyModuleQualifier, e.getMessage())); + logger.error("PrivacyModule %s creation failed: %s.".formatted(privacyModuleQualifier, e.getMessage())); metrics.updateAlertsMetrics(MetricName.general); return null; diff --git a/src/test/java/org/prebid/server/activity/infrastructure/creator/ActivityInfrastructureCreatorTest.java b/src/test/java/org/prebid/server/activity/infrastructure/creator/ActivityInfrastructureCreatorTest.java index bdfa42035da..d0752c1223b 100644 --- a/src/test/java/org/prebid/server/activity/infrastructure/creator/ActivityInfrastructureCreatorTest.java +++ b/src/test/java/org/prebid/server/activity/infrastructure/creator/ActivityInfrastructureCreatorTest.java @@ -209,4 +209,39 @@ public void parseShouldReturnImitatedTransmitEidsActivity() { assertThat(controllers.get(Activity.TRANSMIT_UFPD).isAllowed(null)).isEqualTo(false); assertThat(controllers.get(Activity.TRANSMIT_EIDS).isAllowed(null)).isEqualTo(false); } + + @Test + public void parseShouldSkipRuleThatFailedCreation() { + // given + final Account account = Account.builder() + .privacy(AccountPrivacyConfig.builder() + .activities(Map.of( + Activity.SYNC_USER, AccountActivityConfiguration.of(null, null), + Activity.CALL_BIDDER, AccountActivityConfiguration.of(false, null), + Activity.MODIFY_UFDP, AccountActivityConfiguration.of(true, null), + Activity.TRANSMIT_UFPD, AccountActivityConfiguration.of(true, singletonList( + AccountActivityConditionsRuleConfig.of(null, null))))) + .build()) + .build(); + final GppContext gppContext = GppContextCreator.from(null, null).build().getGppContext(); + + given(activityRuleFactory.from( + same(account.getPrivacy().getActivities().get(Activity.TRANSMIT_UFPD).getRules().getFirst()), + argThat(arg -> arg.getGppContext() == gppContext))) + .willThrow(new IllegalArgumentException()); + + // when + final Map controllers = creator.parse(account, gppContext, debug); + + // then + assertThat(controllers.keySet()).containsExactlyInAnyOrder(Activity.values()); + + assertThat(controllers.get(Activity.SYNC_USER).isAllowed(null)) + .isEqualTo(ActivityInfrastructure.ALLOW_ACTIVITY_BY_DEFAULT); + assertThat(controllers.get(Activity.CALL_BIDDER).isAllowed(null)).isEqualTo(false); + assertThat(controllers.get(Activity.MODIFY_UFDP).isAllowed(null)).isEqualTo(true); + assertThat(controllers.get(Activity.TRANSMIT_UFPD).isAllowed(null)).isEqualTo(true); + + verify(metrics).updateAlertsMetrics(eq(MetricName.general)); + } } diff --git a/src/test/java/org/prebid/server/activity/infrastructure/creator/privacy/uscustomlogic/USCustomLogicModuleCreatorTest.java b/src/test/java/org/prebid/server/activity/infrastructure/creator/privacy/uscustomlogic/USCustomLogicModuleCreatorTest.java index 0a52f8ae86c..cfba2457009 100644 --- a/src/test/java/org/prebid/server/activity/infrastructure/creator/privacy/uscustomlogic/USCustomLogicModuleCreatorTest.java +++ b/src/test/java/org/prebid/server/activity/infrastructure/creator/privacy/uscustomlogic/USCustomLogicModuleCreatorTest.java @@ -16,7 +16,6 @@ import org.prebid.server.activity.infrastructure.privacy.usnat.reader.USNationalGppReader; import org.prebid.server.activity.infrastructure.rule.Rule; import org.prebid.server.auction.gpp.model.GppContextCreator; -import org.prebid.server.exception.InvalidAccountConfigException; import org.prebid.server.json.DecodeException; import org.prebid.server.json.JsonLogic; import org.prebid.server.metric.MetricName; @@ -31,7 +30,6 @@ import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -232,8 +230,11 @@ public void fromShouldThrowExceptionAndEmitMetricsOnInvalidJsonLogicConfig() { singletonList(7), givenConfig(singleton(7), null, Activity.CALL_BIDDER, mapper.createObjectNode())); - // when and then - assertThatExceptionOfType(InvalidAccountConfigException.class).isThrownBy(() -> target.from(creationContext)); + // when + final PrivacyModule privacyModule = target.from(creationContext); + + // then + assertThat(privacyModule.proceed(null)).isEqualTo(Rule.Result.ABSTAIN); verify(jsonLogic).parse(any()); verify(metrics).updateAlertsMetrics(eq(MetricName.general)); diff --git a/src/test/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat/USNatModuleCreatorTest.java b/src/test/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat/USNatModuleCreatorTest.java index 16865ffa5f2..1125110bdc9 100644 --- a/src/test/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat/USNatModuleCreatorTest.java +++ b/src/test/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat/USNatModuleCreatorTest.java @@ -12,6 +12,7 @@ import org.prebid.server.activity.infrastructure.privacy.usnat.reader.USNationalGppReader; import org.prebid.server.activity.infrastructure.rule.Rule; import org.prebid.server.auction.gpp.model.GppContextCreator; +import org.prebid.server.metric.MetricName; import org.prebid.server.metric.Metrics; import org.prebid.server.settings.model.activity.privacy.AccountUSNatModuleConfig; @@ -125,11 +126,36 @@ public void fromShouldShouldSkipConfiguredSectionsIds() { verifyNoMoreInteractions(gppReaderFactory); } + @Test + public void fromShouldShouldSkipSectionsWithInvalidGppSubstring() { + // given + given(gppReaderFactory.forSection(eq(7), any())) + .willReturn(new USNationalGppReader(null) { + + @Override + public Integer getMspaServiceProviderMode() { + throw new IllegalStateException(); + } + }); + + final PrivacyModuleCreationContext creationContext = givenCreationContext(singletonList(7), emptyList()); + + // when + target.from(creationContext); + + // then + verify(gppReaderFactory).forSection(eq(7), any()); + verify(metrics).updateAlertsMetrics(eq(MetricName.general)); + + verifyNoMoreInteractions(gppReaderFactory); + verifyNoMoreInteractions(metrics); + } + private static PrivacyModuleCreationContext givenCreationContext(List sectionsIds, List skipSectionsIds) { return PrivacyModuleCreationContext.of( - Activity.CALL_BIDDER, + Activity.TRANSMIT_UFPD, AccountUSNatModuleConfig.of(true, 0, AccountUSNatModuleConfig.Config.of(skipSectionsIds, false)), GppContextCreator.from(null, sectionsIds).build().getGppContext()); } diff --git a/src/test/java/org/prebid/server/activity/infrastructure/creator/rule/PrivacyModulesRuleCreatorTest.java b/src/test/java/org/prebid/server/activity/infrastructure/creator/rule/PrivacyModulesRuleCreatorTest.java index f3f92de08f0..1ce7438bc5e 100644 --- a/src/test/java/org/prebid/server/activity/infrastructure/creator/rule/PrivacyModulesRuleCreatorTest.java +++ b/src/test/java/org/prebid/server/activity/infrastructure/creator/rule/PrivacyModulesRuleCreatorTest.java @@ -196,6 +196,26 @@ public void fromShouldSkipPrivacyModuleWithoutCreator() { assertThat(rule.proceed(null)).isEqualTo(Rule.Result.ABSTAIN); } + @Test + public void fromShouldSkipPrivacyModuleThatFailedCreation() { + // given + final AccountActivityPrivacyModulesRuleConfig config = AccountActivityPrivacyModulesRuleConfig.of( + singletonList(PrivacyModuleQualifier.US_NAT.moduleName())); + final AccountPrivacyModuleConfig moduleConfig = AccountUSNatModuleConfig.of(null, 0, null); + final ActivityControllerCreationContext creationContext = creationContext( + Map.of(PrivacyModuleQualifier.US_NAT, moduleConfig)); + + given(privacyModuleCreator.from(eq(PrivacyModuleCreationContext.of(null, moduleConfig, null)))) + .willThrow(new IllegalArgumentException()); + + // when + final Rule rule = target.from(config, creationContext); + + // then + assertThat(rule.proceed(null)).isEqualTo(Rule.Result.ABSTAIN); + assertThat(creationContext.isUsed(PrivacyModuleQualifier.US_NAT)).isFalse(); + } + private static ActivityControllerCreationContext creationContext( Map modulesConfigs) { From 6c49d73de731ccca8fda057374f8853fb49a05c3 Mon Sep 17 00:00:00 2001 From: osulzhenko <125548596+osulzhenko@users.noreply.github.com> Date: Mon, 19 May 2025 15:40:30 +0300 Subject: [PATCH 3/4] Test: Invalid GPP string for request handling (#3958) --- .../privacy/GppSyncUserActivitiesSpec.groovy | 203 ++++++-- .../GppTransmitEidsActivitiesSpec.groovy | 343 ++++++++++--- .../GppTransmitUfpdActivitiesSpec.groovy | 484 +++++++++++++++--- .../tests/privacy/PrivacyBaseSpec.groovy | 3 +- 4 files changed, 870 insertions(+), 163 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..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 @@ -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,8 +482,11 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { ] } - def "PBS cookie sync call when privacy module contain invalid GPP string should exclude bidders URLs"() { - given: "Cookie sync request with link to account" + 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 @@ -506,14 +508,68 @@ 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: "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 should not contain any warning" + 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 + it.gpp = invalidGpp + } + + 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 + + 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"() { @@ -613,14 +669,27 @@ 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 contain bidders userSync.urls" assert response.getBidderUserSync(GENERIC).userSync.url + 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 << ["", 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"() { @@ -776,7 +845,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 { @@ -807,17 +879,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"() { @@ -1359,8 +1432,11 @@ 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 @@ -1385,13 +1461,66 @@ 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 + assert metrics[ALERT_GENERAL.value] == 1 + + 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"() { @@ -1502,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) @@ -1509,8 +1641,15 @@ 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 << ["", 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"() { @@ -1680,8 +1819,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 { @@ -1715,17 +1857,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 edd720700ca..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 @@ -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 @@ -194,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 @@ -843,8 +842,11 @@ 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] @@ -865,21 +867,78 @@ 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: "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: "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 + + 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"() { @@ -979,15 +1038,32 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) accountDao.save(account) + and: "Flush metrics" + flushMetrics(activityPbsService) + 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 + + 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 << ["", 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"() { @@ -1144,8 +1220,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 { @@ -1176,16 +1255,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"() { @@ -1304,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 } @@ -1340,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 } @@ -1381,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 } @@ -1418,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 } @@ -1438,7 +1526,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 @@ -1456,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 } @@ -1490,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 } @@ -1529,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 } @@ -1575,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 } @@ -1617,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 @@ -1660,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 @@ -1825,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 @@ -1870,12 +1958,15 @@ 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) - 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 @@ -1901,22 +1992,85 @@ 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 + assert metrics[ALERT_GENERAL.value] == 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 contain consent_string errors" + assert response.ext.errors[PREBID].message == ["Amp request parameter consent_string has invalid format: $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: "Default 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 == 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"() { @@ -1924,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 @@ -1975,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 @@ -2012,12 +2166,70 @@ 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: "Default 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) + + 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 == ampStoredRequest.user.eids + + and: "Response shouldn't contain errors" + assert !response.ext.errors + + 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, ""] + } + 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 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 @@ -2044,14 +2256,20 @@ 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 + 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"() { @@ -2059,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 @@ -2105,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 @@ -2245,8 +2463,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) @@ -2285,17 +2506,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 0eaafecc6f0..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 @@ -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,8 +1108,11 @@ 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] @@ -1131,25 +1133,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) + 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 +1167,82 @@ 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 + assert metrics[ALERT_GENERAL.value] == 1 + + 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"() { @@ -1294,8 +1377,11 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { def account = getAccountWithAllowActivitiesAndPrivacyModule(accountId, activities, [accountGppConfig]) accountDao.save(account) + and: "Flush metrics" + flushMetrics(activityPbsService) + 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) @@ -1321,8 +1407,22 @@ 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 + + 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 << ["", 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"() { @@ -1532,8 +1632,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 { @@ -1564,16 +1667,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"() { @@ -1707,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 } @@ -1762,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 } @@ -1818,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 } @@ -1870,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 } @@ -1890,7 +2022,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 @@ -1908,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 } @@ -1961,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 } @@ -2015,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 } @@ -2076,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 } @@ -2137,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 @@ -2195,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 @@ -2375,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 @@ -2435,12 +2567,15 @@ 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) - 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 @@ -2466,36 +2601,123 @@ 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 + assert metrics[ALERT_GENERAL.value] == 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 contain consent_string errors" + assert response.ext.errors[PREBID].message == ["Amp request parameter consent_string has invalid format: $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: "Default 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"() { @@ -2503,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 @@ -2569,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 @@ -2625,12 +2847,89 @@ 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: "Default 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) + + 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: "Response shouldn't contain errors" + assert !response.ext.errors + + 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, ""] + } + 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 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 @@ -2657,7 +2956,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) @@ -2682,8 +2981,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"() { @@ -2691,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 @@ -2756,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 @@ -2929,8 +3234,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) @@ -2969,17 +3277,43 @@ 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: "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 } 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 aff17c4d49b..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~BVQqAAAAAg.YA" // TODO replace BVQqAAAAAg with ${PBSUtils.getRandomString(7)} when proper fix is ready + 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 fcf4f37a75295e1b9188bd743189e5bc0e9d3623 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Mon, 19 May 2025 17:19:28 +0300 Subject: [PATCH 4/4] fix merge conflicts for functional tests --- .../privacy/GppSyncUserActivitiesSpec.groovy | 10 +++++----- .../GppTransmitEidsActivitiesSpec.groovy | 13 +++++++------ .../GppTransmitUfpdActivitiesSpec.groovy | 19 +++++++------------ 3 files changed, 19 insertions(+), 23 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 01504801806..baac79520c3 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 @@ -57,8 +57,8 @@ import static org.prebid.server.functional.model.config.UsNationalPrivacySection import static org.prebid.server.functional.model.config.UsNationalPrivacySection.SHARING_NOTICE import static org.prebid.server.functional.model.pricefloors.Country.CAN import static org.prebid.server.functional.model.pricefloors.Country.USA -import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ADAPTER_DISALLOWED_COUNT import static org.prebid.server.functional.model.privacy.Metric.PROCESSED_ACTIVITY_RULES_COUNT +import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ADAPTER_DISALLOWED_COUNT import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_REQUEST_DISALLOWED_COUNT import static org.prebid.server.functional.model.request.GppSectionId.USP_V1 import static org.prebid.server.functional.model.request.GppSectionId.US_CA_V1 @@ -519,7 +519,7 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { 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 + assert metrics[ALERT_GENERAL] == 1 and: "Response shouldn't contain warnings" assert !response.warnings @@ -685,7 +685,7 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(cookieSyncRequest, SYNC_USER)] == 1 and: "General alert metric shouldn't be updated" - !metrics[ALERT_GENERAL.getValue()] + !metrics[ALERT_GENERAL] where: regsGpp << [null, "", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] @@ -1473,7 +1473,7 @@ 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 + assert metrics[ALERT_GENERAL] == 1 and: "Logs should contain error" def logs = activityPbsService.getLogsByTime(startTime) @@ -1645,7 +1645,7 @@ class GppSyncUserActivitiesSpec extends PrivacyBaseSpec { assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(setuidRequest, SYNC_USER)] == 1 and: "General alert metric shouldn't be updated" - !metrics[ALERT_GENERAL.getValue()] + !metrics[ALERT_GENERAL] 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 a500f6015a7..3d595ab0612 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 @@ -55,10 +55,10 @@ import static org.prebid.server.functional.model.config.UsNationalPrivacySection import static org.prebid.server.functional.model.config.UsNationalPrivacySection.SHARING_NOTICE import static org.prebid.server.functional.model.pricefloors.Country.CAN import static org.prebid.server.functional.model.pricefloors.Country.USA -import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ACCOUNT_DISALLOWED_COUNT import static org.prebid.server.functional.model.privacy.Metric.ACCOUNT_PROCESSED_RULES_COUNT -import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ADAPTER_DISALLOWED_COUNT import static org.prebid.server.functional.model.privacy.Metric.PROCESSED_ACTIVITY_RULES_COUNT +import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ACCOUNT_DISALLOWED_COUNT +import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ADAPTER_DISALLOWED_COUNT import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_REQUEST_DISALLOWED_COUNT import static org.prebid.server.functional.model.request.GppSectionId.USP_V1 import static org.prebid.server.functional.model.request.GppSectionId.US_CA_V1 @@ -75,6 +75,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 @@ -885,7 +886,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { 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 + assert metrics[ALERT_GENERAL] == 1 and: "Logs should contain error" def logs = activityPbsService.getLogsByTime(startTime) @@ -1058,7 +1059,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { assert metrics[ACCOUNT_PROCESSED_RULES_COUNT.getValue(bidRequest, TRANSMIT_EIDS)] == 1 and: "General alert metric shouldn't be updated" - !metrics[ALERT_GENERAL.getValue()] + !metrics[ALERT_GENERAL] where: regsGpp << [null, "", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] @@ -2003,7 +2004,7 @@ 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 + assert metrics[ALERT_GENERAL] == 1 and: "Response should not contain any warnings" assert !response.ext.warnings @@ -2216,7 +2217,7 @@ class GppTransmitEidsActivitiesSpec extends PrivacyBaseSpec { assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(ampStoredRequest, TRANSMIT_EIDS)] == 1 and: "General alert metric shouldn't be updated" - !metrics[ALERT_GENERAL.getValue()] + !metrics[ALERT_GENERAL] where: regsGpp << [null, ""] 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 b43fd66d69d..dab901bd93d 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 @@ -15,16 +15,10 @@ import org.prebid.server.functional.model.request.amp.AmpRequest import org.prebid.server.functional.model.request.auction.Activity import org.prebid.server.functional.model.request.auction.ActivityRule import org.prebid.server.functional.model.request.auction.AllowActivities -import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.request.auction.Condition -import org.prebid.server.functional.model.request.auction.Data import org.prebid.server.functional.model.request.auction.Device -import org.prebid.server.functional.model.request.auction.Eid import org.prebid.server.functional.model.request.auction.Geo import org.prebid.server.functional.model.request.auction.RegsExt -import org.prebid.server.functional.model.request.auction.User -import org.prebid.server.functional.model.request.auction.UserExt -import org.prebid.server.functional.model.request.auction.UserExtData import org.prebid.server.functional.service.PrebidServerException import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.gpp.UsCaV1Consent @@ -65,10 +59,10 @@ import static org.prebid.server.functional.model.config.UsNationalPrivacySection import static org.prebid.server.functional.model.config.UsNationalPrivacySection.SHARING_NOTICE import static org.prebid.server.functional.model.pricefloors.Country.CAN import static org.prebid.server.functional.model.pricefloors.Country.USA -import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ACCOUNT_DISALLOWED_COUNT import static org.prebid.server.functional.model.privacy.Metric.ACCOUNT_PROCESSED_RULES_COUNT -import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ADAPTER_DISALLOWED_COUNT import static org.prebid.server.functional.model.privacy.Metric.PROCESSED_ACTIVITY_RULES_COUNT +import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ACCOUNT_DISALLOWED_COUNT +import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_ADAPTER_DISALLOWED_COUNT import static org.prebid.server.functional.model.privacy.Metric.TEMPLATE_REQUEST_DISALLOWED_COUNT import static org.prebid.server.functional.model.request.GppSectionId.USP_V1 import static org.prebid.server.functional.model.request.GppSectionId.US_CA_V1 @@ -85,6 +79,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 @@ -1176,7 +1171,7 @@ 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 + assert metrics[ALERT_GENERAL] == 1 and: "Logs should contain error" def logs = activityPbsService.getLogsByTime(startTime) @@ -1423,7 +1418,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { assert metrics[ACCOUNT_PROCESSED_RULES_COUNT.getValue(bidRequest, TRANSMIT_UFPD)] == 1 and: "General alert metric shouldn't be updated" - !metrics[ALERT_GENERAL.getValue()] + !metrics[ALERT_GENERAL] where: regsGpp << [null, "", new UsNatV1Consent.Builder().build(), new UsNatV1Consent.Builder().setGpc(false).build()] @@ -2637,7 +2632,7 @@ 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 + assert metrics[ALERT_GENERAL] == 1 and: "Response should not contain any warnings" assert !response.ext.warnings @@ -2922,7 +2917,7 @@ class GppTransmitUfpdActivitiesSpec extends PrivacyBaseSpec { assert metrics[PROCESSED_ACTIVITY_RULES_COUNT.getValue(ampStoredRequest, TRANSMIT_UFPD)] == 1 and: "General alert metric shouldn't be updated" - !metrics[ALERT_GENERAL.getValue()] + !metrics[ALERT_GENERAL] where: regsGpp << [null, ""]