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 dff0f808435..45df596338e 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 @@ -1,6 +1,8 @@ package org.prebid.server.activity.infrastructure.creator; import org.apache.commons.collections4.ListUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.ObjectUtils; import org.prebid.server.activity.Activity; import org.prebid.server.activity.infrastructure.ActivityController; import org.prebid.server.activity.infrastructure.ActivityInfrastructure; @@ -18,12 +20,14 @@ import org.prebid.server.settings.model.AccountGdprConfig; import org.prebid.server.settings.model.AccountPrivacyConfig; import org.prebid.server.settings.model.GdprConfig; +import org.prebid.server.settings.model.HostActivityInfrastructureConfiguration; import org.prebid.server.settings.model.Purpose; import org.prebid.server.settings.model.PurposeEid; 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 org.prebid.server.util.ListUtil; import java.util.Arrays; import java.util.Collections; @@ -48,16 +52,19 @@ public class ActivityInfrastructureCreator { private static final int MODULE_MAX_SKIP_RATE = 100; private final ActivityRuleFactory activityRuleFactory; + private final HostActivityInfrastructureConfiguration hostConfig; private final Purpose defaultPurpose4; private final Metrics metrics; private final JacksonMapper jacksonMapper; public ActivityInfrastructureCreator(ActivityRuleFactory activityRuleFactory, + HostActivityInfrastructureConfiguration hostConfig, GdprConfig gdprConfig, Metrics metrics, JacksonMapper jacksonMapper) { this.activityRuleFactory = Objects.requireNonNull(activityRuleFactory); + this.hostConfig = hostConfig; this.defaultPurpose4 = Optional.ofNullable(gdprConfig) .map(GdprConfig::getPurposes) .map(Purposes::getP4) @@ -76,7 +83,8 @@ private ActivityInfrastructureDebug debugWheel(Account account, TraceLevel trace } Map parse(Account account, GppContext gppContext, ActivityInfrastructureDebug debug) { - final Optional accountPrivacyConfig = Optional.ofNullable(account.getPrivacy()); + final Optional accountPrivacyConfig = + Optional.ofNullable(mergeWithHost(account.getPrivacy())); final Map activitiesConfiguration = accountPrivacyConfig .map(AccountPrivacyConfig::getActivities) @@ -109,10 +117,44 @@ Map parse(Account account, GppContext gppContext, skipPrivacyModules, gppContext, debug)), - (oldValue, newValue) -> oldValue, + (oldValue, _) -> oldValue, enumMapFactory())); } + private AccountPrivacyConfig mergeWithHost(AccountPrivacyConfig accountPrivacyConfig) { + if (hostConfig == null) { + return accountPrivacyConfig; + } + if (accountPrivacyConfig == null) { + return AccountPrivacyConfig.builder().activities(hostConfig.getActivities()).build(); + } + + final Map activities = new EnumMap<>(Activity.class); + for (Activity activity : Activity.values()) { + final AccountActivityConfiguration merged = mergeWithHost( + MapUtils.emptyIfNull(accountPrivacyConfig.getActivities()).get(activity), + MapUtils.emptyIfNull(hostConfig.getActivities()).get(activity)); + + if (merged != null) { + activities.put(activity, merged); + } + } + + return accountPrivacyConfig.toBuilder().activities(activities).build(); + } + + private static AccountActivityConfiguration mergeWithHost(AccountActivityConfiguration account, + AccountActivityConfiguration host) { + + return account == null || host == null + ? ObjectUtils.getIfNull(account, host) + : AccountActivityConfiguration.of( + ObjectUtils.getIfNull(account.getAllow(), host.getAllow()), + ListUtil.union( + ListUtils.emptyIfNull(account.getRules()), + ListUtils.emptyIfNull(host.getRules()))); + } + private BinaryOperator takeFirstAndLogDuplicates(String accountId) { return (first, second) -> { logger.warn("Duplicate configuration found for privacy module %s for account %s" diff --git a/src/main/java/org/prebid/server/settings/model/HostActivityInfrastructureConfiguration.java b/src/main/java/org/prebid/server/settings/model/HostActivityInfrastructureConfiguration.java new file mode 100644 index 00000000000..e5ccda1295b --- /dev/null +++ b/src/main/java/org/prebid/server/settings/model/HostActivityInfrastructureConfiguration.java @@ -0,0 +1,15 @@ +package org.prebid.server.settings.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Value; +import org.prebid.server.activity.Activity; +import org.prebid.server.settings.model.activity.AccountActivityConfiguration; + +import java.util.Map; + +@Value(staticConstructor = "of") +public class HostActivityInfrastructureConfiguration { + + @JsonProperty("allowactivities") + Map activities; +} 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 d6561b8fe32..569a6a6d68d 100644 --- a/src/main/java/org/prebid/server/spring/config/ActivityInfrastructureConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/ActivityInfrastructureConfiguration.java @@ -14,7 +14,10 @@ import org.prebid.server.json.JsonLogic; import org.prebid.server.metric.Metrics; import org.prebid.server.settings.model.GdprConfig; +import org.prebid.server.settings.model.HostActivityInfrastructureConfiguration; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -87,17 +90,28 @@ PrivacyModulesRuleCreator privacyModulesRuleCreator(List p } } + @Bean + @ConditionalOnProperty("activity-infrastructure") + HostActivityInfrastructureConfiguration hostActivityInfrastructureConfiguration( + @Value("activity-infrastructure") String configAsString, + JacksonMapper mapper) { + + return mapper.decodeValue(configAsString, HostActivityInfrastructureConfiguration.class); + } + @Bean ActivityRuleFactory activityRuleFactory(List> ruleCreators) { return new ActivityRuleFactory(ruleCreators); } @Bean - ActivityInfrastructureCreator activityInfrastructureCreator(ActivityRuleFactory activityRuleFactory, - GdprConfig gdprConfig, - Metrics metrics, - JacksonMapper jacksonMapper) { - - return new ActivityInfrastructureCreator(activityRuleFactory, gdprConfig, metrics, jacksonMapper); + ActivityInfrastructureCreator activityInfrastructureCreator( + ActivityRuleFactory activityRuleFactory, + @Autowired(required = false) HostActivityInfrastructureConfiguration hostConfig, + GdprConfig gdprConfig, + Metrics metrics, + JacksonMapper jacksonMapper) { + + return new ActivityInfrastructureCreator(activityRuleFactory, hostConfig, gdprConfig, metrics, jacksonMapper); } } diff --git a/src/test/java/org/prebid/server/activity/infrastructure/ActivityControllerTest.java b/src/test/java/org/prebid/server/activity/infrastructure/ActivityControllerTest.java index fe5c57ca744..6f2c404eac4 100644 --- a/src/test/java/org/prebid/server/activity/infrastructure/ActivityControllerTest.java +++ b/src/test/java/org/prebid/server/activity/infrastructure/ActivityControllerTest.java @@ -26,7 +26,7 @@ public class ActivityControllerTest { @Test public void isAllowedShouldReturnDefaultResultIfAllRulesReturnsAbstain() { // given - final Rule rule = TestRule.disallowIfMatches(payload -> false); + final Rule rule = TestRule.noMatchAndDisallow(); final ActivityController activityController = ActivityController.of(true, singletonList(rule), debug); // when @@ -44,9 +44,10 @@ public void isAllowedShouldReturnFalse() { final ActivityController activityController = ActivityController.of( true, asList( - TestRule.allowIfMatches(payload -> false), - TestRule.disallowIfMatches(payload -> true), - TestRule.disallowIfMatches(payload -> false)), + TestRule.noMatchAndAllow(), + TestRule.noMatchAndDisallow(), + TestRule.matchAndDisallow(), + TestRule.matchAndAllow()), debug); // when @@ -55,7 +56,7 @@ public void isAllowedShouldReturnFalse() { // then assertThat(result).isFalse(); verify(debug).emitActivityInvocationDefaultResult(true); - verify(debug, times(2)).emitProcessedRule(any(), any()); + verify(debug, times(3)).emitProcessedRule(any(), any()); } @Test @@ -64,9 +65,10 @@ public void isAllowedShouldReturnTrue() { final ActivityController activityController = ActivityController.of( false, asList( - TestRule.allowIfMatches(payload -> false), - TestRule.allowIfMatches(payload -> true), - TestRule.disallowIfMatches(payload -> false)), + TestRule.noMatchAndAllow(), + TestRule.noMatchAndDisallow(), + TestRule.matchAndAllow(), + TestRule.matchAndDisallow()), debug); // when @@ -75,6 +77,6 @@ public void isAllowedShouldReturnTrue() { // then assertThat(result).isTrue(); verify(debug).emitActivityInvocationDefaultResult(false); - verify(debug, times(2)).emitProcessedRule(any(), any()); + verify(debug, times(3)).emitProcessedRule(any(), any()); } } 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 d0752c1223b..f5a6247d239 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 @@ -19,13 +19,14 @@ import org.prebid.server.settings.model.Account; import org.prebid.server.settings.model.AccountGdprConfig; import org.prebid.server.settings.model.AccountPrivacyConfig; +import org.prebid.server.settings.model.HostActivityInfrastructureConfiguration; import org.prebid.server.settings.model.Purpose; import org.prebid.server.settings.model.PurposeEid; import org.prebid.server.settings.model.Purposes; import org.prebid.server.settings.model.activity.AccountActivityConfiguration; import org.prebid.server.settings.model.activity.privacy.AccountUSCustomLogicModuleConfig; import org.prebid.server.settings.model.activity.privacy.AccountUSNatModuleConfig; -import org.prebid.server.settings.model.activity.rule.AccountActivityConditionsRuleConfig; +import org.prebid.server.settings.model.activity.rule.AccountActivityRuleConfig; import java.util.Map; @@ -37,6 +38,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.prebid.server.activity.infrastructure.privacy.PrivacyModuleQualifier.US_NAT; @@ -59,7 +61,7 @@ public class ActivityInfrastructureCreatorTest { @BeforeEach public void setUp() { - creator = new ActivityInfrastructureCreator(activityRuleFactory, null, metrics, jacksonMapper); + creator = new ActivityInfrastructureCreator(activityRuleFactory, null, null, metrics, jacksonMapper); } @Test @@ -91,8 +93,7 @@ public void parseShouldSkipPrivacyModulesDuplicatesAndEmitWarnings() { // given final Account account = Account.builder() .privacy(AccountPrivacyConfig.builder() - .activities(Map.of(Activity.SYNC_USER, AccountActivityConfiguration.of( - null, singletonList(AccountActivityConditionsRuleConfig.of(null, null))))) + .activities(Map.of(Activity.SYNC_USER, activityConfigurationWithRule(null))) .modules(asList( AccountUSNatModuleConfig.of(null, 0, null), AccountUSNatModuleConfig.of(null, 0, null))) @@ -112,8 +113,7 @@ public void parseShouldPopulateSkipConfigForModules() { // given final Account account = Account.builder() .privacy(AccountPrivacyConfig.builder() - .activities(Map.of(Activity.SYNC_USER, AccountActivityConfiguration.of( - null, singletonList(AccountActivityConditionsRuleConfig.of(null, null))))) + .activities(Map.of(Activity.SYNC_USER, activityConfigurationWithRule(null))) .modules(asList( AccountUSNatModuleConfig.of(null, 100, null), AccountUSCustomLogicModuleConfig.of(null, 0, null))) @@ -136,11 +136,10 @@ public void parseShouldReturnExpectedResult() { 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))))) + Activity.SYNC_USER, activityConfiguration(null), + Activity.CALL_BIDDER, activityConfiguration(false), + Activity.MODIFY_UFDP, activityConfiguration(true), + Activity.TRANSMIT_UFPD, activityConfigurationWithRule(true))) .build()) .build(); final GppContext gppContext = GppContextCreator.from(null, null).build().getGppContext(); @@ -148,7 +147,7 @@ public void parseShouldReturnExpectedResult() { given(activityRuleFactory.from( same(account.getPrivacy().getActivities().get(Activity.TRANSMIT_UFPD).getRules().getFirst()), argThat(arg -> arg.getGppContext() == gppContext))) - .willReturn(TestRule.disallowIfMatches(payload -> true)); + .willReturn(TestRule.matchAndDisallow()); // when final Map controllers = creator.parse(account, gppContext, debug); @@ -168,7 +167,7 @@ public void parseShouldReturnOriginalTransmitEidsActivity() { // given final Account account = Account.builder() .privacy(AccountPrivacyConfig.builder() - .activities(Map.of(Activity.TRANSMIT_UFPD, AccountActivityConfiguration.of(false, null))) + .activities(Map.of(Activity.TRANSMIT_UFPD, activityConfiguration(false))) .build()) .build(); final GppContext gppContext = GppContextCreator.from(null, null).build().getGppContext(); @@ -194,7 +193,7 @@ public void parseShouldReturnImitatedTransmitEidsActivity() { .p4(Purpose.of(null, null, null, PurposeEid.of(true, false, null))) .build()) .build()) - .activities(Map.of(Activity.TRANSMIT_UFPD, AccountActivityConfiguration.of(false, null))) + .activities(Map.of(Activity.TRANSMIT_UFPD, activityConfiguration(false))) .build()) .build(); final GppContext gppContext = GppContextCreator.from(null, null).build().getGppContext(); @@ -216,11 +215,10 @@ public void parseShouldSkipRuleThatFailedCreation() { 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))))) + Activity.SYNC_USER, activityConfiguration(null), + Activity.CALL_BIDDER, activityConfiguration(false), + Activity.MODIFY_UFDP, activityConfiguration(true), + Activity.TRANSMIT_UFPD, activityConfigurationWithRule(false))) .build()) .build(); final GppContext gppContext = GppContextCreator.from(null, null).build().getGppContext(); @@ -240,8 +238,105 @@ public void parseShouldSkipRuleThatFailedCreation() { .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); + assertThat(controllers.get(Activity.TRANSMIT_UFPD).isAllowed(null)).isEqualTo(false); verify(metrics).updateAlertsMetrics(eq(MetricName.general)); } + + @Test + public void parseShouldUseHostConfigIfAccountConfigAbsent() { + // given + creator = new ActivityInfrastructureCreator( + activityRuleFactory, + HostActivityInfrastructureConfiguration.of(Map.of(Activity.SYNC_USER, activityConfiguration(false))), + null, + metrics, + jacksonMapper); + + final GppContext gppContext = GppContextCreator.from(null, null).build().getGppContext(); + + // when + final Map controllers = + creator.parse(Account.builder().build(), gppContext, debug); + + // then + assertThat(controllers.keySet()).containsExactlyInAnyOrder(Activity.values()); + assertThat(controllers.get(Activity.SYNC_USER).isAllowed(null)).isEqualTo(false); + assertThat(controllers.get(Activity.CALL_BIDDER).isAllowed(null)).isEqualTo(true); + } + + @Test + public void parseShouldOverrideAccountConfigNullsWithHostConfig() { + // given + creator = new ActivityInfrastructureCreator( + activityRuleFactory, + HostActivityInfrastructureConfiguration.of(Map.of( + Activity.SYNC_USER, activityConfiguration(false), + Activity.CALL_BIDDER, activityConfiguration(true), + Activity.MODIFY_UFDP, activityConfigurationWithRule(true))), + null, + metrics, + jacksonMapper); + + final Account account = Account.builder() + .privacy(AccountPrivacyConfig.builder() + .activities(Map.of( + Activity.SYNC_USER, activityConfiguration(null), + Activity.CALL_BIDDER, activityConfiguration(false), + Activity.MODIFY_UFDP, activityConfiguration(true))) + .build()) + .build(); + final GppContext gppContext = GppContextCreator.from(null, null).build().getGppContext(); + + given(activityRuleFactory.from(any(), any())).willReturn(TestRule.matchAndDisallow()); + + // 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(false); + assertThat(controllers.get(Activity.CALL_BIDDER).isAllowed(null)).isEqualTo(false); + assertThat(controllers.get(Activity.MODIFY_UFDP).isAllowed(null)).isEqualTo(false); + assertThat(controllers.get(Activity.TRANSMIT_UFPD).isAllowed(null)).isEqualTo(true); + } + + @Test + public void parseShouldMergeAccountConfigWithHostConfig() { + // given + creator = new ActivityInfrastructureCreator( + activityRuleFactory, + HostActivityInfrastructureConfiguration.of( + Map.of(Activity.SYNC_USER, activityConfigurationWithRule(true))), + null, + metrics, + jacksonMapper); + + final Account account = Account.builder() + .privacy(AccountPrivacyConfig.builder() + .activities(Map.of(Activity.SYNC_USER, activityConfigurationWithRule(true))) + .build()) + .build(); + final GppContext gppContext = GppContextCreator.from(null, null).build().getGppContext(); + + given(activityRuleFactory.from(any(), any())) + .willReturn(TestRule.noMatchAndAllow(), TestRule.matchAndDisallow()); + + // 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(false); + assertThat(controllers.get(Activity.CALL_BIDDER).isAllowed(null)).isEqualTo(true); + } + + private static AccountActivityConfiguration activityConfiguration(Boolean allow) { + return AccountActivityConfiguration.of(allow, null); + } + + private static AccountActivityConfiguration activityConfigurationWithRule(Boolean allow) { + final AccountActivityRuleConfig ruleConfig = mock(); + return AccountActivityConfiguration.of(allow, singletonList(ruleConfig)); + } } diff --git a/src/test/java/org/prebid/server/activity/infrastructure/debug/ActivityInfrastructureDebugTest.java b/src/test/java/org/prebid/server/activity/infrastructure/debug/ActivityInfrastructureDebugTest.java index 2a86b5962c4..49954faa850 100644 --- a/src/test/java/org/prebid/server/activity/infrastructure/debug/ActivityInfrastructureDebugTest.java +++ b/src/test/java/org/prebid/server/activity/infrastructure/debug/ActivityInfrastructureDebugTest.java @@ -118,7 +118,7 @@ public void emitProcessedRuleShouldReturnExpectedResultIfTraceLevelIsNull() { final ActivityInfrastructureDebug debug = debug(null); // when - debug.emitProcessedRule(TestRule.allowIfMatches(payload -> true), Rule.Result.ALLOW); + debug.emitProcessedRule(TestRule.matchAndAllow(), Rule.Result.ALLOW); // then assertThat(debug.trace()).isEmpty(); @@ -132,7 +132,7 @@ public void emitProcessedRuleShouldReturnExpectedResultIfTraceLevelIsBasic() { final ActivityInfrastructureDebug debug = debug(TraceLevel.basic); // when - debug.emitProcessedRule(TestRule.allowIfMatches(payload -> true), Rule.Result.ALLOW); + debug.emitProcessedRule(TestRule.matchAndAllow(), Rule.Result.ALLOW); // then assertThat(debug.trace()).containsExactly(ExtTraceActivityRule.of( @@ -149,7 +149,7 @@ public void emitProcessedRuleShouldReturnExpectedResultIfTraceLevelIsVerbose() { final ActivityInfrastructureDebug debug = debug(TraceLevel.verbose); // when - debug.emitProcessedRule(TestRule.allowIfMatches(payload -> true), Rule.Result.ALLOW); + debug.emitProcessedRule(TestRule.matchAndAllow(), Rule.Result.ALLOW); // then assertThat(debug.trace()).containsExactly(ExtTraceActivityRule.of( diff --git a/src/test/java/org/prebid/server/activity/infrastructure/rule/AndRuleTest.java b/src/test/java/org/prebid/server/activity/infrastructure/rule/AndRuleTest.java index f89e5d70c6b..9fd221793fb 100644 --- a/src/test/java/org/prebid/server/activity/infrastructure/rule/AndRuleTest.java +++ b/src/test/java/org/prebid/server/activity/infrastructure/rule/AndRuleTest.java @@ -82,9 +82,7 @@ public void proceedShouldReturnAbstain() { @Test public void asLogEntryShouldReturnExpectedObjectNode() { // given - final AndRule rule = new AndRule(asList( - TestRule.allowIfMatches(payload -> true), - TestRule.disallowIfMatches(payload -> false))); + final AndRule rule = new AndRule(asList(TestRule.matchAndAllow(), TestRule.noMatchAndAllow())); // when final JsonNode result = rule.asLogEntry(mapper); diff --git a/src/test/java/org/prebid/server/activity/infrastructure/rule/TestRule.java b/src/test/java/org/prebid/server/activity/infrastructure/rule/TestRule.java index 996eb645b44..5d2e9b5800a 100644 --- a/src/test/java/org/prebid/server/activity/infrastructure/rule/TestRule.java +++ b/src/test/java/org/prebid/server/activity/infrastructure/rule/TestRule.java @@ -6,30 +6,35 @@ import org.prebid.server.activity.infrastructure.debug.Loggable; import org.prebid.server.activity.infrastructure.payload.ActivityInvocationPayload; -import java.util.Objects; -import java.util.function.Predicate; - public class TestRule extends AbstractMatchRule implements Loggable { - private final Predicate predicate; + private final boolean matches; private final boolean allowed; - private TestRule(Predicate predicate, boolean allowed) { - this.predicate = Objects.requireNonNull(predicate); + private TestRule(boolean matches, boolean allowed) { + this.matches = matches; this.allowed = allowed; } - public static TestRule allowIfMatches(Predicate predicate) { - return new TestRule(predicate, true); + public static TestRule noMatchAndAllow() { + return new TestRule(false, true); + } + + public static TestRule matchAndAllow() { + return new TestRule(true, true); + } + + public static TestRule noMatchAndDisallow() { + return new TestRule(false, false); } - public static TestRule disallowIfMatches(Predicate predicate) { - return new TestRule(predicate, false); + public static TestRule matchAndDisallow() { + return new TestRule(true, false); } @Override public boolean matches(ActivityInvocationPayload activityInvocationPayload) { - return predicate.test(activityInvocationPayload); + return matches; } @Override