From 02ab85c582034b2957ac4cc4a47de967749a6b81 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Fri, 11 Jul 2025 13:21:46 +0300 Subject: [PATCH 01/18] Changed result function config field from config to args, fixed filtering result function. --- .../modules/rule/engine/core/config/StageConfigParser.java | 2 +- .../rule/engine/core/config/model/ResultFunctionConfig.java | 2 +- .../rule/engine/core/request/RequestSpecification.java | 4 +--- .../result/functions/filter/ExcludeBiddersFunction.java | 3 ++- .../result/functions/filter/FilterBiddersFunction.java | 4 ++++ .../result/functions/filter/IncludeBiddersFunction.java | 2 +- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java index 72a1de19dc1..83380947b84 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/StageConfigParser.java @@ -141,7 +141,7 @@ private List> parseActions(List RuleAction.of( config.getFunction(), specification.resultFunctionByName(config.getFunction()), - config.getConfig())) + config.getArgs())) .toList(); actions.forEach(this::validateActionConfig); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java index 9daf2811a76..2b642c633e2 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/config/model/ResultFunctionConfig.java @@ -8,5 +8,5 @@ public class ResultFunctionConfig { String function; - ObjectNode config; + ObjectNode args; } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java index a5a7d724957..264e3f489e5 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/RequestSpecification.java @@ -5,7 +5,6 @@ import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestResultContext; import org.prebid.server.hooks.modules.rule.engine.core.request.context.RequestSchemaContext; -import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.LogATagFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter.ExcludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.result.functions.filter.IncludeBiddersFunction; import org.prebid.server.hooks.modules.rule.engine.core.request.schema.functions.AdUnitCodeFunction; @@ -75,8 +74,7 @@ public RequestSpecification(ObjectMapper mapper, resultFunctions = Map.of( ExcludeBiddersFunction.NAME, new ExcludeBiddersFunction(mapper, bidderCatalog), - IncludeBiddersFunction.NAME, new IncludeBiddersFunction(mapper, bidderCatalog), - LogATagFunction.NAME, new LogATagFunction(mapper)); + IncludeBiddersFunction.NAME, new IncludeBiddersFunction(mapper, bidderCatalog)); } public SchemaFunction schemaFunctionByName(String name) { diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java index 02637aa8e36..72a1a5760d3 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/ExcludeBiddersFunction.java @@ -25,12 +25,13 @@ protected FilterBiddersResult filterBidders(Imp imp, final Set removedBidders = new HashSet<>(); final ObjectNode updatedExt = imp.getExt().deepCopy(); + for (String bidder : bidders) { if (ifSyncedId != null && ifSyncedId != isBidderIdSynced(bidder, uidsCookie)) { continue; } - updatedExt.remove(bidder); + removeBidder(updatedExt, bidder); removedBidders.add(bidder); } diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java index ff1d7e4e49f..e0bfb9efc40 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunction.java @@ -95,6 +95,10 @@ protected boolean isBidderIdSynced(String bidder, UidsCookie uidsCookie) { .orElse(false); } + protected void removeBidder(ObjectNode impExt, String bidder) { + ((ObjectNode) impExt.get("prebid").get("bidder")).remove(bidder); + } + @Override public void validateConfig(ObjectNode config) { final FilterBiddersFunctionConfig parsedConfig = parseConfig(config); diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java index 86807a18754..30462e2a4a2 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/IncludeBiddersFunction.java @@ -30,7 +30,7 @@ protected FilterBiddersResult filterBidders(Imp imp, for (String bidder : IteratorUtils.asIterable(ext.fieldNames())) { if (!bidders.contains(bidder)) { - updatedExt.remove(bidder); + removeBidder(updatedExt, bidder); removedBidders.add(bidder); } } From 8ec56a11c037aa003e5609b3dfd4f2bf5c299284 Mon Sep 17 00:00:00 2001 From: markiian Date: Thu, 17 Jul 2025 15:28:47 +0300 Subject: [PATCH 02/18] Initial commit of rule engine functional test --- .../filter/FilterBiddersFunctionConfig.java | 1 + pom.xml | 2 +- .../server/functional/model/ModuleName.groovy | 1 + .../config/ModuleHookImplementation.groovy | 1 + .../model/config/PbRulesEngine.groovy | 16 + .../model/config/PbsModulesConfig.groovy | 1 + .../model/config/ResultFunction.groovy | 22 + .../model/config/RuleEngineArguments.groovy | 11 + .../model/config/RuleEngineFunction.groovy | 45 + .../config/RuleEngineFunctionArgs.groovy | 35 + .../config/RuleEngineModelDefault.groovy | 7 + .../config/RuleEngineModelDefaultArgs.groovy | 6 + .../model/config/RuleEngineModelRule.groovy | 17 + .../config/RuleEngineModelRuleResult.groovy | 36 + .../RuleEngineModelRuleResultsArgs.groovy | 26 + .../model/config/RuleEngineModelSchema.groovy | 88 + .../functional/model/config/RuleSets.groovy | 23 + .../model/config/RuleVariables.groovy | 39 + .../config/RulesEngineModelGroups.groovy | 31 + .../model/request/auction/Device.groovy | 2 +- .../model/request/auction/DeviceType.groovy | 22 + .../model/request/auction/Kvps.groovy | 6 + .../model/request/auction/Prebid.groovy | 1 + .../response/auction/AnalyticResult.groovy | 3 +- .../auction/BidRejectionReason.groovy | 1 + .../model/response/auction/ImpResult.groovy | 3 +- .../model/response/auction/ModuleValue.groovy | 21 +- .../model/response/auction/SeatNonBid.groovy | 3 +- .../tests/AlternateBidderCodeSpec.groovy | 14 +- .../functional/tests/BidderParamsSpec.groovy | 4 +- .../functional/tests/SeatNonBidSpec.groovy | 18 +- .../tests/module/AbTestingModuleSpec.groovy | 38 +- .../tests/module/ModuleBaseSpec.groovy | 27 +- .../ortb2blocking/Ortb2BlockingSpec.groovy | 2 +- .../module/pbruleengine/RuleEngineSpec.groovy | 4393 +++++++++++++++++ .../richmedia/RichMediaFilterSpec.groovy | 12 +- .../pricefloors/PriceFloorsRulesSpec.groovy | 4 +- .../functional/tests/privacy/DsaSpec.groovy | 8 +- .../tests/privacy/GdprAuctionSpec.groovy | 2 +- 39 files changed, 4933 insertions(+), 59 deletions(-) create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/PbRulesEngine.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/ResultFunction.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineArguments.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelDefault.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelDefaultArgs.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRule.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResult.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelSchema.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleSets.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleVariables.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroups.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/request/auction/DeviceType.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/request/auction/Kvps.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java index 780943fe996..c5e355eebe9 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java @@ -19,6 +19,7 @@ public class FilterBiddersFunctionConfig { @JsonProperty("seatnonbid") BidRejectionReason seatNonBid = BidRejectionReason.REQUEST_BLOCKED_OPTIMIZED; + @JsonProperty("ifSyncedId") Boolean ifSyncedId; @JsonProperty("analyticsValue") diff --git a/pom.xml b/pom.xml index 2617ea9fcc1..7be92568153 100644 --- a/pom.xml +++ b/pom.xml @@ -367,7 +367,7 @@ ${maven-surefire-plugin.version} - false + true ${skipUnitTests} diff --git a/src/test/groovy/org/prebid/server/functional/model/ModuleName.groovy b/src/test/groovy/org/prebid/server/functional/model/ModuleName.groovy index 2bc06ab7144..ee036e4c703 100644 --- a/src/test/groovy/org/prebid/server/functional/model/ModuleName.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/ModuleName.groovy @@ -8,6 +8,7 @@ enum ModuleName { PB_RESPONSE_CORRECTION ("pb-response-correction"), ORTB2_BLOCKING("ortb2-blocking"), PB_REQUEST_CORRECTION('pb-request-correction'), + RULE_ENGINE('rule-engine') @JsonValue final String code diff --git a/src/test/groovy/org/prebid/server/functional/model/config/ModuleHookImplementation.groovy b/src/test/groovy/org/prebid/server/functional/model/config/ModuleHookImplementation.groovy index 247bdea4353..cb74da0fb17 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/ModuleHookImplementation.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/ModuleHookImplementation.groovy @@ -11,6 +11,7 @@ enum ModuleHookImplementation { ORTB2_BLOCKING_BIDDER_REQUEST("ortb2-blocking-bidder-request"), ORTB2_BLOCKING_RAW_BIDDER_RESPONSE("ortb2-blocking-raw-bidder-response"), PB_REQUEST_CORRECTION_PROCESSED_AUCTION_REQUEST("pb-request-correction-processed-auction-request"), + PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST("rule-engine-processed-auction-request") @JsonValue final String code diff --git a/src/test/groovy/org/prebid/server/functional/model/config/PbRulesEngine.groovy b/src/test/groovy/org/prebid/server/functional/model/config/PbRulesEngine.groovy new file mode 100644 index 00000000000..01696cff06d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/PbRulesEngine.groovy @@ -0,0 +1,16 @@ +package org.prebid.server.functional.model.config + +class PbRulesEngine { + + Boolean enabled + Boolean generateRulesFromBidderConfig + List ruleSets + + static PbRulesEngine createRulesEngineWithRule(Boolean enabled = true) { + new PbRulesEngine().tap { + it.enabled = enabled + it.generateRulesFromBidderConfig = false + it.ruleSets = [RuleSets.createRuleSets()] + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/PbsModulesConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/PbsModulesConfig.groovy index 59f640f966c..dbdddbf1473 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/PbsModulesConfig.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/PbsModulesConfig.groovy @@ -13,4 +13,5 @@ class PbsModulesConfig { Ortb2BlockingConfig ortb2Blocking PbResponseCorrection pbResponseCorrection PbRequestCorrectionConfig pbRequestCorrection + PbRulesEngine ruleEngine } diff --git a/src/test/groovy/org/prebid/server/functional/model/config/ResultFunction.groovy b/src/test/groovy/org/prebid/server/functional/model/config/ResultFunction.groovy new file mode 100644 index 00000000000..11383006d2d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/ResultFunction.groovy @@ -0,0 +1,22 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonValue + +enum ResultFunction { + + INCLUDE_BIDDERS("includeBidders"), + EXCLUDE_BIDDER("excludeBidders"), + LOG_A_TAG("logAtag") + + private String value + + ResultFunction(String value) { + this.value = value + } + + @Override + @JsonValue + String toString() { + return value + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineArguments.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineArguments.groovy new file mode 100644 index 00000000000..20f5855be7b --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineArguments.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.config + +import org.prebid.server.functional.model.bidder.BidderName + +class RuleEngineArguments { + + List bidders + Integer seatNonBid + Boolean ifSyncedId + String analyticsValue +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy new file mode 100644 index 00000000000..3a53dfe9ebe --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy @@ -0,0 +1,45 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonValue + +enum RuleEngineFunction { + + DEVICE_COUNTRY("deviceCountry"), + DEVICE_COUNTRY_IN("deviceCountryIn"), + DATA_CENTER("dataCenter"), + DATA_CENTER_IN("dataCenter"), + CHANNEL("channel"), + EID_AVAILABLE("eidAvailable"), + EID_IN("eidIn"), + USER_FPD_AVAILABLE("userFpdAvailable"), + FPD_AVAILABLE("fpdAvail"), + GPP_SID_AVAILABLE("gppSidAvailable"), + GPP_SID_IN("gppSidIn"), + TCF_IN_SCOPE("tcfInScope"), + PERCENT("percent"), + PREBID_KEY("prebidKey"), + DOMAIN("domain"), + DOMAIN_IN("domainIn"), + BUNDLE("bundle"), + BUNDLE_IN("bundleIn"), + MEDIA_TYPE_IN("mediaTypeIn"), + AD_UNIT_CODE("adUnitCode"), + AD_UNIT_CODE_IN("adUnitCodeIn"), + DEVICE_TYPE("deviceType"), + DEVICE_TYPE_IN("deviceTypeIn"), + BID_PRICE("bidPrice"), + + IMP_ID("impId") + + private String value + + RuleEngineFunction(String value) { + this.value = value + } + + @JsonValue + @Override + String toString() { + return value + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy new file mode 100644 index 00000000000..edc97471c35 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy @@ -0,0 +1,35 @@ +package org.prebid.server.functional.model.config + +import org.prebid.server.functional.util.PBSUtils + +class RuleEngineFunctionArgs { + + List countries + List datacenters + List sources + List sids + Object ptc + String key + List domains + List bundles + List codes + List types + String operator + BigDecimal value + Currency currency + + static RuleEngineFunctionArgs getDefaultFunctionArgs() { + new RuleEngineFunctionArgs().tap { + countries = [PBSUtils.randomString] + datacenters = [PBSUtils.randomString] + sources = [PBSUtils.randomString] + sids = [PBSUtils.randomNumber] + ptc = PBSUtils.randomString + key = PBSUtils.randomString + domains = [PBSUtils.randomString] + bundles = [PBSUtils.randomString] + codes = [PBSUtils.randomString] + types = [PBSUtils.randomString] + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelDefault.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelDefault.groovy new file mode 100644 index 00000000000..7193bb920dd --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelDefault.groovy @@ -0,0 +1,7 @@ +package org.prebid.server.functional.model.config + +class RuleEngineModelDefault { + + ResultFunction function + RuleEngineModelDefaultArgs args +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelDefaultArgs.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelDefaultArgs.groovy new file mode 100644 index 00000000000..3d7884926de --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelDefaultArgs.groovy @@ -0,0 +1,6 @@ +package org.prebid.server.functional.model.config + +class RuleEngineModelDefaultArgs { + + String analyticsValue +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRule.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRule.groovy new file mode 100644 index 00000000000..557fc999e61 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRule.groovy @@ -0,0 +1,17 @@ +package org.prebid.server.functional.model.config + +import static java.lang.Boolean.TRUE +import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithExcludeResult + +class RuleEngineModelRule { + + List conditions + List results + + static RuleEngineModelRule createRuleEngineModelRule() { + new RuleEngineModelRule().tap { + it.conditions = [TRUE as String] + it.results = [createRuleEngineModelRuleWithExcludeResult()] + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResult.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResult.groovy new file mode 100644 index 00000000000..e548a93b874 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResult.groovy @@ -0,0 +1,36 @@ +package org.prebid.server.functional.model.config + +import org.prebid.server.functional.model.bidder.BidderName + +import static org.prebid.server.functional.model.bidder.BidderName.ACEEX +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.config.ResultFunction.EXCLUDE_BIDDER +import static org.prebid.server.functional.model.config.ResultFunction.INCLUDE_BIDDERS +import static org.prebid.server.functional.model.config.ResultFunction.LOG_A_TAG + +class RuleEngineModelRuleResult { + + ResultFunction function + RuleEngineModelRuleResultsArgs args + + static RuleEngineModelRuleResult createRuleEngineModelRuleWithIncludeResult(BidderName bidderName = ACEEX) { + new RuleEngineModelRuleResult().tap { + it.function = INCLUDE_BIDDERS + it.args = RuleEngineModelRuleResultsArgs.createRuleEngineModelRuleResultsArgs(bidderName) + } + } + + static RuleEngineModelRuleResult createRuleEngineModelRuleWithExcludeResult(BidderName bidderName = OPENX) { + new RuleEngineModelRuleResult().tap { + it.function = EXCLUDE_BIDDER + it.args = RuleEngineModelRuleResultsArgs.createRuleEngineModelRuleResultsArgs(bidderName) + } + } + + static RuleEngineModelRuleResult createRuleEngineModelRuleWithLogATagResult() { + new RuleEngineModelRuleResult().tap { + it.function = LOG_A_TAG + it.args = RuleEngineModelRuleResultsArgs.createRuleEngineModelRuleResultsArgsOnlyATag() + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy new file mode 100644 index 00000000000..4d5c177b427 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy @@ -0,0 +1,26 @@ +package org.prebid.server.functional.model.config + +import org.prebid.server.functional.model.bidder.BidderName +import org.prebid.server.functional.util.PBSUtils + +class RuleEngineModelRuleResultsArgs { + + List bidders + Integer seatNonBid + String analyticsValue + Boolean ifSyncedId + + static RuleEngineModelRuleResultsArgs createRuleEngineModelRuleResultsArgs(BidderName bidderName){ + new RuleEngineModelRuleResultsArgs().tap { + it.bidders = [bidderName] + it.analyticsValue = PBSUtils.randomString + it.seatNonBid = 201 + } + } + + static RuleEngineModelRuleResultsArgs createRuleEngineModelRuleResultsArgsOnlyATag(){ + new RuleEngineModelRuleResultsArgs().tap { + it.analyticsValue = PBSUtils.randomString + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelSchema.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelSchema.groovy new file mode 100644 index 00000000000..49893f5afc9 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelSchema.groovy @@ -0,0 +1,88 @@ +package org.prebid.server.functional.model.config + +import groovy.transform.ToString +import org.prebid.server.functional.model.pricefloors.MediaType +import org.prebid.server.functional.model.request.GppSectionId +import org.prebid.server.functional.util.PBSUtils + +import static org.prebid.server.functional.model.config.RuleEngineFunction.AD_UNIT_CODE_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.BUNDLE_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.DATA_CENTER_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_TYPE_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.DOMAIN_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.EID_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.GPP_SID_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.MEDIA_TYPE_IN +import static org.prebid.server.functional.model.pricefloors.Country.USA + +@ToString(includeNames = true, ignoreNulls = true) +class RuleEngineModelSchema { + + RuleEngineFunction function + RuleEngineFunctionArgs args + + static RuleEngineModelSchema createDeviceCountryInSchema(List argsCountries = [USA]) { + new RuleEngineModelSchema().tap { + it.function = DEVICE_COUNTRY_IN + it.args = new RuleEngineFunctionArgs(countries: argsCountries) + } + } + + static RuleEngineModelSchema createDataCenterInSchema(List argsDataCenter = [PBSUtils.randomString]) { + new RuleEngineModelSchema().tap { + it.function = DATA_CENTER_IN + it.args = new RuleEngineFunctionArgs(datacenters: argsDataCenter) + } + } + + static RuleEngineModelSchema createEidInSchema(List argsEid = [PBSUtils.randomString]) { + new RuleEngineModelSchema().tap { + it.function = EID_IN + it.args = new RuleEngineFunctionArgs(sources: argsEid) + } + } + + static RuleEngineModelSchema createGppSidInSchema(List argsGppSid = [PBSUtils.getRandomEnum(GppSectionId)]) { + new RuleEngineModelSchema().tap { + it.function = GPP_SID_IN + it.args = new RuleEngineFunctionArgs(sids: argsGppSid) + } + } + + static RuleEngineModelSchema createDomainInSchema(List argsDomain = [PBSUtils.randomString]) { + new RuleEngineModelSchema().tap { + it.function = DOMAIN_IN + it.args = new RuleEngineFunctionArgs(domains: argsDomain) + } + } + + static RuleEngineModelSchema createBundleInSchema(List argsBundle = [PBSUtils.randomString]) { + new RuleEngineModelSchema().tap { + it.function = BUNDLE_IN + it.args = new RuleEngineFunctionArgs(bundles: argsBundle) + } + } + + static RuleEngineModelSchema createMediaTypeInSchema(List argsMediaType = [PBSUtils.getRandomEnum(MediaType)]) { + new RuleEngineModelSchema().tap { + it.function = MEDIA_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: argsMediaType) + } + } + + static RuleEngineModelSchema createAdUnitInSchema(List argsMediaType = [PBSUtils.randomString]) { + new RuleEngineModelSchema().tap { + it.function = AD_UNIT_CODE_IN + it.args = new RuleEngineFunctionArgs(codes: argsMediaType) + } + } + + static RuleEngineModelSchema createDeviceTypeInSchema(List argsMediaType = [PBSUtils.randomNumber]) { + new RuleEngineModelSchema().tap { + it.function = DEVICE_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: argsMediaType) + } + } + +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleSets.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleSets.groovy new file mode 100644 index 00000000000..7027b160e28 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleSets.groovy @@ -0,0 +1,23 @@ +package org.prebid.server.functional.model.config + +import static org.prebid.server.functional.model.config.Stage.PROCESSED_AUCTION_REQUEST +import static org.prebid.server.functional.util.PBSUtils.randomString + +class RuleSets { + + Boolean enabled + Stage stage + String name + String version + List modelGroups + + static RuleSets createRuleSets() { + new RuleSets().tap { + it.enabled = true + it.stage = PROCESSED_AUCTION_REQUEST + it.name = randomString + it.version = randomString + it.modelGroups = [RulesEngineModelGroups.createRulesModuleGroup()] + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleVariables.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleVariables.groovy new file mode 100644 index 00000000000..f1a91d2475a --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleVariables.groovy @@ -0,0 +1,39 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonValue + + +enum RuleVariables { + + DEVICE_COUNTRY("deviceCountry"), + DEVICE_COUNTRY_SET("deviceCountrySet"), + DATACENTER("datacenters"), + CHANNEL("channel"), + BUYER_UID_AVAILABLE("buyeruidAvailable"), + ANY_ID_AVAILABLE("anyIdAvailable"), + ANY_ID_OR_USER_FPD_AVAILABLE("anyIdOrUserFpdAvailable"), + ANY_ID_OR_FPD_AVAIL("anyIdOrFpdAvail"), + GPP_SID("gppSid"), + TCF_IN_SCOPE("tcfInScope"), + PERCENT("percent"), + DOMAIN("domain"), + BUNDLE("bundle"), + MEDIA_TYPES("mediaTypes"), + AD_UNIT_CODE("adUnitCode"), + DEVICE_TYPE("deviceType"), + BID_PRICE("bidPrice"), + MACRO("macro"), + INVALID("invalid") + + private String value + + RuleVariables(String value) { + this.value = value + } + + @Override + @JsonValue + String toString() { + return value + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroups.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroups.groovy new file mode 100644 index 00000000000..61d1807910d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroups.groovy @@ -0,0 +1,31 @@ +package org.prebid.server.functional.model.config + +import com.fasterxml.jackson.annotation.JsonProperty +import org.prebid.server.functional.model.pricefloors.Country +import org.prebid.server.functional.util.PBSUtils + +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY_IN +import static org.prebid.server.functional.model.config.RuleEngineModelRule.* +import static org.prebid.server.functional.model.config.RuleEngineModelSchema.createDeviceCountryInSchema + +class RulesEngineModelGroups { + + Integer weight + String version + String analyticsKey + List schema + @JsonProperty("default") + List modelDefault + List rules + + static RulesEngineModelGroups createRulesModuleGroup() { + new RulesEngineModelGroups().tap { + it.weight = PBSUtils.getRandomNumber(0, 100) + it.version = PBSUtils.randomString + it.analyticsKey = PBSUtils.randomString + it.schema = [createDeviceCountryInSchema()] + it.modelDefault = [] + it.rules = [createRuleEngineModelRule()] + } + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Device.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Device.groovy index fbce486ac47..4ac87e72285 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/Device.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Device.groovy @@ -12,7 +12,7 @@ class Device { UserAgent sua String ip String ipv6 - Integer devicetype + DeviceType devicetype String make String model String os diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/DeviceType.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/DeviceType.groovy new file mode 100644 index 00000000000..b33855723d7 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/DeviceType.groovy @@ -0,0 +1,22 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.annotation.JsonValue + +enum DeviceType { + + MOBILE_TABLET_GENERAL(1), + PERSONAL_COMPUTER(2), + CONNECTED_TV(3), + PHONE(4), + TABLET(5), + CONNECTED_DEVICE(6), + SET_TOP_BOX(7), + OOH_DEVICE(8) + + @JsonValue + final Integer value + + DeviceType(Integer value) { + this.value = value + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Kvps.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Kvps.groovy new file mode 100644 index 00000000000..f1706a2b220 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Kvps.groovy @@ -0,0 +1,6 @@ +package org.prebid.server.functional.model.request.auction + +class Kvps { + + String key +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy index 499dccea5a3..c79c2a285b5 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy @@ -45,6 +45,7 @@ class Prebid { PaaFormat paaFormat @JsonProperty("alternatebiddercodes") AlternateBidderCodes alternateBidderCodes + Kvps kvps static class Channel { diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/AnalyticResult.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/AnalyticResult.groovy index 7976ae24c2f..a8b737a848a 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/AnalyticResult.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/AnalyticResult.groovy @@ -8,6 +8,7 @@ import org.prebid.server.functional.model.request.auction.FetchStatus import org.prebid.server.functional.model.request.auction.Imp import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS_BLOCK @ToString(includeNames = true, ignoreNulls = true) @JsonNaming(PropertyNamingStrategies.LowerCaseStrategy) @@ -20,7 +21,7 @@ class AnalyticResult { static AnalyticResult buildFromImp(Imp imp) { def appliedTo = new AppliedTo(impIds: [imp.id], bidders: [imp.ext.prebid.bidder.configuredBidders.first()]) - def impResult = new ImpResult(status: 'success-block', values: new ModuleValue(richmediaFormat: 'mraid'), appliedTo: appliedTo) + def impResult = new ImpResult(status: SUCCESS_BLOCK, values: new ModuleValue(richmediaFormat: 'mraid'), appliedTo: appliedTo) new AnalyticResult(name: 'reject-richmedia', status: SUCCESS, results: [impResult]) } } diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/BidRejectionReason.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidRejectionReason.groovy index 2fb7d75bbf7..2c8007113bf 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/BidRejectionReason.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidRejectionReason.groovy @@ -14,6 +14,7 @@ enum BidRejectionReason { REQUEST_BLOCKED_GENERAL(200), REQUEST_BLOCKED_UNSUPPORTED_CHANNEL(201), REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE(202), + REQUEST_BIDDER_REMOVED_BY_MODULE(203), REQUEST_BLOCKED_PRIVACY(204), REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY(205), diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/ImpResult.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/ImpResult.groovy index de157de7775..8740e415f47 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/ImpResult.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/ImpResult.groovy @@ -4,13 +4,14 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.databind.annotation.JsonNaming import groovy.transform.EqualsAndHashCode import groovy.transform.ToString +import org.prebid.server.functional.model.request.auction.FetchStatus @ToString(includeNames = true, ignoreNulls = true) @JsonNaming(PropertyNamingStrategies.LowerCaseStrategy) @EqualsAndHashCode class ImpResult { - String status + FetchStatus status ModuleValue values AppliedTo appliedTo } diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy index 9a1e9d1b440..48f51ed81a1 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy @@ -1,16 +1,35 @@ package org.prebid.server.functional.model.response.auction +import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.databind.annotation.JsonNaming import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import org.prebid.server.functional.model.ModuleName +import org.prebid.server.functional.model.config.ResultFunction +import org.prebid.server.functional.model.bidder.BidderName @ToString(includeNames = true, ignoreNulls = true) -@JsonNaming(PropertyNamingStrategies.KebabCaseStrategy) @EqualsAndHashCode class ModuleValue { ModuleName module String richmediaFormat + @JsonProperty("analytics_key") + String analyticsKey + @JsonProperty("analytics_value") + String analyticsValue + @JsonProperty("model_version") + String modelVersion + @JsonProperty("condition_fired") + List conditionFired + @JsonProperty("rule_fired") + String rule_fired + @JsonProperty("result_functions") + List resultFunctions + @JsonProperty("bidders_removed") + List biddersRemoved + @JsonProperty("seatnonbid") + String seatNonBid + String message } diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/SeatNonBid.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/SeatNonBid.groovy index 16e1fd46459..a30917a18a6 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/SeatNonBid.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/SeatNonBid.groovy @@ -3,11 +3,12 @@ package org.prebid.server.functional.model.response.auction import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.databind.annotation.JsonNaming import groovy.transform.ToString +import org.prebid.server.functional.model.bidder.BidderName @ToString(includeNames = true, ignoreNulls = true) @JsonNaming(PropertyNamingStrategies.LowerCaseStrategy) class SeatNonBid { - String seat + BidderName seat List nonBid } diff --git a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy index f47efd346f8..1e2c67fad6f 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/AlternateBidderCodeSpec.groovy @@ -274,7 +274,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == bidderName.value + assert seatNonBid.seat == bidderName assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -329,7 +329,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == bidderName.value + assert seatNonBid.seat == bidderName assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -787,7 +787,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == UNKNOWN.value + assert seatNonBid.seat == UNKNOWN assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -843,7 +843,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == UNKNOWN.value + assert seatNonBid.seat == UNKNOWN assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -1187,7 +1187,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == requestedAllowedBidderCode.value + assert seatNonBid.seat == requestedAllowedBidderCode assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -1242,7 +1242,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == requestedAllowedBidderCode.value + assert seatNonBid.seat == requestedAllowedBidderCode assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL @@ -1303,7 +1303,7 @@ class AlternateBidderCodeSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == allowedBidderCodes.value + assert seatNonBid.seat == allowedBidderCodes assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL diff --git a/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy index 0db60125603..c563796496e 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy @@ -1145,7 +1145,7 @@ class BidderParamsSpec extends BaseSpec { ["No match between the configured currencies and bidRequest.cur"] def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == BidderName.GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY @@ -1242,7 +1242,7 @@ class BidderParamsSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == BidderName.ALIAS.value + assert seatNonBid.seat == BidderName.ALIAS assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY diff --git a/src/test/groovy/org/prebid/server/functional/tests/SeatNonBidSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/SeatNonBidSpec.groovy index f6b02f22e76..88d2d32a6b9 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/SeatNonBidSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/SeatNonBidSpec.groovy @@ -1,6 +1,7 @@ package org.prebid.server.functional.tests import org.mockserver.model.HttpStatusCode +import org.prebid.server.functional.model.bidder.BidderName import org.prebid.server.functional.model.config.AccountAuctionConfig import org.prebid.server.functional.model.config.AccountBidValidationConfig import org.prebid.server.functional.model.config.AccountConfig @@ -22,6 +23,7 @@ import static org.mockserver.model.HttpStatusCode.OK_200 import static org.mockserver.model.HttpStatusCode.PROCESSING_102 import static org.mockserver.model.HttpStatusCode.SERVICE_UNAVAILABLE_503 import static org.prebid.server.functional.model.AccountStatus.ACTIVE + import static org.prebid.server.functional.model.config.BidValidationEnforcement.ENFORCE import static org.prebid.server.functional.model.request.auction.DebugCondition.DISABLED import static org.prebid.server.functional.model.request.auction.DebugCondition.ENABLED @@ -57,7 +59,7 @@ class SeatNonBidSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == ERROR_NO_BID @@ -83,7 +85,7 @@ class SeatNonBidSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == ERROR_INVALID_BID_RESPONSE } @@ -105,7 +107,7 @@ class SeatNonBidSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == ERROR_BIDDER_UNREACHABLE } @@ -138,7 +140,7 @@ class SeatNonBidSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_INVALID_CREATIVE_SIZE } @@ -169,7 +171,7 @@ class SeatNonBidSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_INVALID_CREATIVE_NOT_SECURE @@ -216,7 +218,7 @@ class SeatNonBidSpec extends BaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == ERROR_NO_BID @@ -277,7 +279,7 @@ class SeatNonBidSpec extends BaseSpec { assert seatNonBids.size() == 1 def seatNonBid = seatNonBids[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == ERROR_TIMED_OUT } @@ -301,7 +303,7 @@ class SeatNonBidSpec extends BaseSpec { assert seatNonBids.size() == 1 def seatNonBid = seatNonBids[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/AbTestingModuleSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/AbTestingModuleSpec.groovy index 73e44cc3232..1582f3b200d 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/AbTestingModuleSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/AbTestingModuleSpec.groovy @@ -219,7 +219,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_ACTION, NO_ACTION] it.analyticsTags.activities.name.flatten().sort() == [ORTB2_BLOCKING, AB_TESTING, AB_TESTING].value.sort() it.analyticsTags.activities.status.flatten().sort() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS, FetchStatus.SUCCESS].sort() - it.analyticsTags.activities.results.status.flatten().sort() == [FetchStatus.SUCCESS_ALLOW, FetchStatus.RUN, FetchStatus.RUN].value.sort() + it.analyticsTags.activities.results.status.flatten().sort() == [FetchStatus.SUCCESS_ALLOW, FetchStatus.RUN, FetchStatus.RUN].sort() it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -230,7 +230,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_ACTION] it.analyticsTags.activities.name.flatten() == [AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.RUN].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.RUN] it.analyticsTags.activities.results.values.module.flatten() == [PB_RESPONSE_CORRECTION] } @@ -280,7 +280,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -291,7 +291,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [PB_RESPONSE_CORRECTION] } @@ -341,7 +341,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -352,7 +352,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_ACTION] it.analyticsTags.activities.name.flatten() == [AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.RUN].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.RUN] it.analyticsTags.activities.results.values.module.flatten() == [PB_RESPONSE_CORRECTION] } @@ -395,7 +395,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -433,7 +433,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_ACTION, NO_ACTION] it.analyticsTags.activities.name.flatten().sort() == [ORTB2_BLOCKING, AB_TESTING, AB_TESTING].value.sort() it.analyticsTags.activities.status.flatten().sort() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS, FetchStatus.SUCCESS].sort() - it.analyticsTags.activities.results.status.flatten().sort() == [FetchStatus.SUCCESS_ALLOW, FetchStatus.RUN, FetchStatus.RUN].value.sort() + it.analyticsTags.activities.results.status.flatten().sort() == [FetchStatus.SUCCESS_ALLOW, FetchStatus.RUN, FetchStatus.RUN].sort() it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -482,7 +482,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -529,7 +529,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -577,7 +577,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.analyticsTags.activities.name.flatten().sort() == [ORTB2_BLOCKING, AB_TESTING, AB_TESTING].value.sort() it.analyticsTags.activities.status.flatten().sort() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS, FetchStatus.SUCCESS].sort() - it.analyticsTags.activities.results.status.flatten().sort() == [FetchStatus.SUCCESS_ALLOW, FetchStatus.RUN, FetchStatus.RUN].value.sort() + it.analyticsTags.activities.results.status.flatten().sort() == [FetchStatus.SUCCESS_ALLOW, FetchStatus.RUN, FetchStatus.RUN].sort() it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -626,7 +626,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -796,7 +796,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -859,7 +859,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -923,7 +923,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -934,7 +934,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [PB_RESPONSE_CORRECTION] } @@ -983,7 +983,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.analyticsTags.activities.name.flatten() == [ORTB2_BLOCKING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SUCCESS_ALLOW].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SUCCESS_ALLOW] it.analyticsTags.activities.results.values.module.flatten().every { it == null } } @@ -1052,7 +1052,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION, NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING, AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS, FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED, FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED, FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } @@ -1121,7 +1121,7 @@ class AbTestingModuleSpec extends ModuleBaseSpec { it.action == [NO_INVOCATION, NO_INVOCATION] it.analyticsTags.activities.name.flatten() == [AB_TESTING, AB_TESTING].value it.analyticsTags.activities.status.flatten() == [FetchStatus.SUCCESS, FetchStatus.SUCCESS] - it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED].value + it.analyticsTags.activities.results.status.flatten() == [FetchStatus.SKIPPED, FetchStatus.SKIPPED] it.analyticsTags.activities.results.values.module.flatten() == [ModuleName.ORTB2_BLOCKING, ModuleName.ORTB2_BLOCKING] } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy index 453de43aa3c..4b010516d5d 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy @@ -3,12 +3,15 @@ package org.prebid.server.functional.tests.module import org.prebid.server.functional.model.config.Endpoint import org.prebid.server.functional.model.config.ExecutionPlan import org.prebid.server.functional.model.config.Stage +import org.prebid.server.functional.model.response.auction.AnalyticResult +import org.prebid.server.functional.model.response.auction.BidResponse +import org.prebid.server.functional.model.response.auction.InvocationResult import org.prebid.server.functional.tests.BaseSpec import static org.prebid.server.functional.model.ModuleName.ORTB2_BLOCKING -import static org.prebid.server.functional.model.ModuleName.PB_REQUEST_CORRECTION import static org.prebid.server.functional.model.ModuleName.PB_RESPONSE_CORRECTION import static org.prebid.server.functional.model.ModuleName.PB_RICHMEDIA_FILTER +import static org.prebid.server.functional.model.ModuleName.RULE_ENGINE import static org.prebid.server.functional.model.config.Endpoint.OPENRTB2_AUCTION import static org.prebid.server.functional.model.config.Stage.ALL_PROCESSED_BID_RESPONSES import static org.prebid.server.functional.model.config.Stage.PROCESSED_AUCTION_REQUEST @@ -56,8 +59,24 @@ class ModuleBaseSpec extends BaseSpec { ["hooks.${ORTB2_BLOCKING.code}.enabled": isEnabled as String] } - protected static Map getRequestCorrectionSettings(Endpoint endpoint = OPENRTB2_AUCTION, Stage stage = PROCESSED_AUCTION_REQUEST) { - ["hooks.${PB_REQUEST_CORRECTION.code}.enabled": "true", - "hooks.host-execution-plan" : encode(ExecutionPlan.getSingleEndpointExecutionPlan(endpoint, PB_REQUEST_CORRECTION, [stage]))] + protected static Map getRulesEngineSettings(Endpoint endpoint = OPENRTB2_AUCTION, Stage stage = PROCESSED_AUCTION_REQUEST) { + ["hooks.${RULE_ENGINE.code}.enabled" : "true", + "hooks.${RULE_ENGINE.code}.rule-cache.expire-after-minutes": "123123123123", + "hooks.${RULE_ENGINE.code}.rule-cache.max-size" : "200", + "hooks.rule-engine.rule-parsing.retry-initial-delay-millis": "1", + "hooks.rule-engine.rule-parsing.retry-max-delay-millis" : "1", + "hooks.rule-engine.rule-parsing.retry-exponential-factor" : "1.2", + "hooks.rule-engine.rule-parsing.retry-exponential-jitter" : "1.2", + "hooks.host-execution-plan" : encode(ExecutionPlan.getSingleEndpointExecutionPlan(endpoint, RULE_ENGINE, [stage]))] + } + + protected static List getAnalyticResults(BidResponse response) { + response.ext.prebid.modules?.trace?.stages?.first() + ?.outcomes?.first()?.groups?.first() + ?.invocationResults?.first()?.analyticsTags?.activities + } + + protected static List getInvocationResult(BidResponse response) { + response.ext.prebid.modules?.trace?.stages?.first()?.outcomes?.first()?.groups?.first()?.invocationResults } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/ortb2blocking/Ortb2BlockingSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/ortb2blocking/Ortb2BlockingSpec.groovy index 2b2de98750b..a7a97bc8816 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/ortb2blocking/Ortb2BlockingSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/ortb2blocking/Ortb2BlockingSpec.groovy @@ -1521,7 +1521,7 @@ class Ortb2BlockingSpec extends ModuleBaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == ErrorType.GENERIC.value + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_ADVERTISER_BLOCKED } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy new file mode 100644 index 00000000000..8a6a93a43b9 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy @@ -0,0 +1,4393 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.ChannelType +import org.prebid.server.functional.model.UidsCookie +import org.prebid.server.functional.model.bidder.Generic +import org.prebid.server.functional.model.bidder.Openx +import org.prebid.server.functional.model.config.AccountConfig +import org.prebid.server.functional.model.config.AccountHooksConfiguration +import org.prebid.server.functional.model.config.PbRulesEngine +import org.prebid.server.functional.model.config.PbsModulesConfig +import org.prebid.server.functional.model.config.RuleEngineFunctionArgs +import org.prebid.server.functional.model.config.RuleEngineModelDefault +import org.prebid.server.functional.model.config.RuleEngineModelDefaultArgs +import org.prebid.server.functional.model.config.RuleEngineModelSchema +import org.prebid.server.functional.model.config.RuleSets +import org.prebid.server.functional.model.config.RulesEngineModelGroups +import org.prebid.server.functional.model.config.Stage +import org.prebid.server.functional.model.db.Account +import org.prebid.server.functional.model.db.StoredImp +import org.prebid.server.functional.model.pricefloors.Country +import org.prebid.server.functional.model.pricefloors.MediaType +import org.prebid.server.functional.model.request.GppSectionId +import org.prebid.server.functional.model.request.auction.Amx +import org.prebid.server.functional.model.request.auction.AppExt +import org.prebid.server.functional.model.request.auction.AppExtData +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.Content +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.DeviceType +import org.prebid.server.functional.model.request.auction.DistributionChannel +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.Imp +import org.prebid.server.functional.model.request.auction.ImpExt +import org.prebid.server.functional.model.request.auction.ImpExtContextData +import org.prebid.server.functional.model.request.auction.ImpExtPrebid +import org.prebid.server.functional.model.request.auction.Kvps +import org.prebid.server.functional.model.request.auction.PrebidStoredRequest +import org.prebid.server.functional.model.request.auction.Publisher +import org.prebid.server.functional.model.request.auction.Regs +import org.prebid.server.functional.model.request.auction.SiteExt +import org.prebid.server.functional.model.request.auction.SiteExtData +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.model.response.auction.BidRejectionReason +import org.prebid.server.functional.model.response.auction.ResponseAction +import org.prebid.server.functional.service.PrebidServerService +import org.prebid.server.functional.tests.module.ModuleBaseSpec +import org.prebid.server.functional.util.HttpUtil +import org.prebid.server.functional.util.PBSUtils +import spock.lang.IgnoreRest + +import static java.lang.Boolean.FALSE +import static java.lang.Boolean.TRUE +import static org.prebid.server.functional.model.ChannelType.WEB +import static org.prebid.server.functional.model.ModuleName.RULE_ENGINE +import static org.prebid.server.functional.model.bidder.BidderName.ALIAS +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.bidder.BidderName.OPENX_ALIAS +import static org.prebid.server.functional.model.bidder.BidderName.UNKNOWN +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.ResultFunction.LOG_A_TAG +import static org.prebid.server.functional.model.config.RuleEngineFunction.AD_UNIT_CODE +import static org.prebid.server.functional.model.config.RuleEngineFunction.AD_UNIT_CODE_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.BUNDLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.BUNDLE_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.CHANNEL +import static org.prebid.server.functional.model.config.RuleEngineFunction.DATA_CENTER_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_TYPE +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_TYPE_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.DOMAIN +import static org.prebid.server.functional.model.config.RuleEngineFunction.DOMAIN_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.EID_AVAILABLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.EID_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.FPD_AVAILABLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.GPP_SID_AVAILABLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.GPP_SID_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.MEDIA_TYPE_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.PERCENT +import static org.prebid.server.functional.model.config.RuleEngineFunction.PREBID_KEY +import static org.prebid.server.functional.model.config.RuleEngineFunction.TCF_IN_SCOPE +import static org.prebid.server.functional.model.config.RuleEngineFunction.USER_FPD_AVAILABLE +import static org.prebid.server.functional.model.config.RuleEngineModelRule.createRuleEngineModelRule +import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithExcludeResult +import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithIncludeResult +import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithLogATagResult +import static org.prebid.server.functional.model.config.RuleEngineModelSchema.createDeviceCountryInSchema +import static org.prebid.server.functional.model.pricefloors.Country.BULGARIA +import static org.prebid.server.functional.model.pricefloors.Country.USA +import static org.prebid.server.functional.model.pricefloors.MediaType.BANNER +import static org.prebid.server.functional.model.request.auction.DistributionChannel.APP +import static org.prebid.server.functional.model.request.auction.DistributionChannel.DOOH +import static org.prebid.server.functional.model.request.auction.DistributionChannel.SITE +import static org.prebid.server.functional.model.request.auction.FetchStatus.ERROR +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.request.auction.Imp.getDefaultImpression +import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.ERROR_NO_BID +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_MODULE +import static org.prebid.server.functional.model.response.auction.MediaType.* +import static org.prebid.server.functional.testcontainers.Dependencies.getNetworkServiceContainer + +class RuleEngineSpec extends ModuleBaseSpec { + + private static final Integer BIDDERS_REQUESTED = 3 + private static final Integer ONE_BIDDER_REQUESTED = 1 + private static final String WILDCARD = "*" + private static final Map OPENX_CONFIG = ["adapters.${OPENX}.enabled" : "true", + "adapters.${OPENX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + private static final Map AMX_CONFIG = ["adapters.${AMX}.enabled" : "true", + "adapters.${AMX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + private static final Map OPENX_ALIAS_CONFIG = ["adapters.${OPENX}.aliases.${OPENX_ALIAS}.enabled" : "true", + "adapters.${OPENX}.aliases.${OPENX_ALIAS}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + private static final String dataCenter = PBSUtils.randomString + private static final Map DATA_CENTER = ['datacenter-region': dataCenter] + private static final PrebidServerService pbsServiceWithRulesEngineModule = pbsServiceFactory.getService( + getRulesEngineSettings() + AMX_CONFIG + OPENX_CONFIG + OPENX_ALIAS_CONFIG) + + //todo test cases: DONE + // If 'exclude', then remove these bidders from imp[n].ext.prebid.bidders + // If 'include', then keep only these bidders in imp[n].ext.prebid.bidders + // include case with 2imps and same bidder what it goes + // with alias include / exclude + // few imps + // with ifSyncedId true/false | HALF DONE -> only for exclude + // without analytics key | DONE + // seatNonBid --- default 203 and specified any code + + //todo: PROGRESS + // same weight -> then random I hope we don't tests it since we don't know exactly result and it's potential flaky tests NO-OP + // test case with default + // send seanNonBid when it really needed + + //todo: + // Loop through every imp in the auction + // If adUnitCode was one of the schema functions, and its winning value was other than a wildcard: + // Compare the chosen adUnitCode value against the following ORTB fields: imp[n].ext.gpid, imp[n].tagid, imp[n].ext.data.pbadslot, imp[n].ext.prebid.storedrequest.id + // If none of them match, then skip this imp. + // If mediaType was one of the schema functions, and its winning value was other than a wildcard: + // Compare the chosen mediaType value against the existence of imp.MEDIATYPE, + // If there's no imp.MEDIATYPE, then skip this imp. + + def "PBS shouldn't remove bidder when rule engine not fully configured in account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with enabled rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), pbRulesEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + + and: "Analytics result shouldn't contain info about rule engine" + assert getAnalyticResults(bidResponse).isEmpty() + + where: + pbRulesEngine << [ + createRulesEngineWithRule().tap { it.ruleSets = [] }, + createRulesEngineWithRule().tap { it.ruleSets[0].stage = null }, + createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema = [] }, + createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules = [] }, + createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results = [] }, + createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].conditions = [] }, + ] + } + + def "PBS shouldn't remove bidder and emit a warning when args rule engine not fully configured in account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with enabled rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), + createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results[0].args = null }) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.withWarmup().sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + + and: "Invocation result should contain warning of rule engine" + assert getInvocationResult(bidResponse)[0].message == "Function 'excludeBidders' configuration is invalid: Configuration is required, but not provided" + } + + + def "PBS shouldn't remove bidder and emit a warning when model group rule engine not fully configured in account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with enabled rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), + createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups = [] },) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + + and: "Invocation result should contain warning of rule engine" + assert getInvocationResult(bidResponse)[0].message == "Weighted list cannot be empty" + } + + //todo what should happened when updateBidRequestWithTraceVerboseAndReturnAllBidStatus absent and validation failed + + def "PBS should logAtag when default model doesn't exist"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.device = new Device(geo: new Geo(country: BULGARIA)) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].modelDefault = null + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == ERROR + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def impResult = it.results[0] + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == [LOG_A_TAG.toString()] + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.appliedTo.impIds == [WILDCARD] + } + } + + //todo : need to fix from dev + def "PBS shouldn't remove bidder when rule engine disabled or absent in account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with disabled or without rules engine " + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + + and: "Analytics result shouldn't contain info about rule engine" + assert getAnalyticResults(bidResponse).isEmpty() + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + where: + pbRuleEngine << [null, createRulesEngineWithRule(false)] + } + + def "PBS shouldn't remove bidder when rule sets disabled in account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with disabled rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets.first.enabled = false + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + } + + def "PBS should remove bidder and not update analytics when bidder matched with conditions and without analytics value"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets without analytics value" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.analyticsValue = null + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain two seat" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + + and: "Response should contain seat bid" + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + } + + def "PBS should remove bidder from imps and use default 203 value for seatNonBid when seatNonBid null and exclude bidder in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + } + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.seatNonBid = null + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat == [OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == 2 + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should populate seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == SUCCESS + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def impResult = it.results[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + } + + def "PBS should remove bidder from imps and update seatNonBid with other code when seatNonBid override and exclude bidder in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + } + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def bidRejectionReason = PBSUtils.getRandomEnum(BidRejectionReason) + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.seatNonBid = bidRejectionReason.code + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat == [OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == 2 + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should populate seatNon bid" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == bidRejectionReason + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == SUCCESS + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def impResult = it.results[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + } + + def "PBS should remove bidder from imps and not update seatNonBid when returnAllBidStatus disabled and exclude bidder in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + } + updateBidRequestWithGeoCountry(it) + ext.prebid.tap { + returnAllBidStatus = false + trace = VERBOSE + } + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.seatNonBid = ERROR_NO_BID.code + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == 2 + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response shouldn't populate seatNon bid with code 203" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == SUCCESS + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def impResult = it.results[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + } + + @IgnoreRest + def "PBS shouldn't remove bidder and don't update analytics when bidder doesn't specified with account and request"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(UNKNOWN), + createRuleEngineModelRuleWithIncludeResult(UNKNOWN)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about rule engine" //todo WHY HERE Updated + assert getInvocationResult(bidResponse)[0].action == ResponseAction.NO_INVOCATION + } + + @IgnoreRest + def "PBS should include one bidder and update analytics when multiple bidders specified and one included in account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(OPENX)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == SUCCESS + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def impResult = it.results[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == [GENERIC, AMX] + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + } + + def "PBS should remove bidder by device geo from imps when bidder excluded in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + } + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == 2 + + and: "Bid response should contain seatBids" + assert bidResponse.seatbid.seat == [OPENX, AMX] + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should populate seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == SUCCESS + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def impResult = it.results[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == [GENERIC, GENERIC] + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + } + + def "PBS should leave only include bidder at imps when bidder include in account config"() { + given: "Bid request with multiply imps bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + } + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED + + and: "Bid response should contain seatBid.bids" + assert bidResponse.seatbid[0].bid.size() == 2 + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == SUCCESS + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def impResult = it.results[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == [OPENX, AMX, OPENX, AMX] + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + } + + def "PBS should only logATag when function log a tag only"() { + given: "Bid request with multiply imps bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + } + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithLogATagResult()] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == 3 + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == SUCCESS + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def impResult = it.results[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.modelVersion == groups.version + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + } + + //todo: Hard alias + + def "PBS should leave only hard alias bidder at imps when hard alias bidder include in account config"() { + given: "Bid request with multiply imps bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + openxAlias = Openx.defaultOpenx + } + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(OPENX_ALIAS)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED + + and: "Bid response should contain seatBid.bids" + assert bidResponse.seatbid[0].bid.size() == 1 + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + def seatNonBid = bidResponse.ext.seatnonbid + assert seatNonBid.seat == [OPENX, AMX, GENERIC] + assert seatNonBid.nonBid[0].impId == [bidRequest.imp[0].id] + assert seatNonBid.nonBid[0].statusCode == [REQUEST_BIDDER_REMOVED_BY_MODULE] + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == SUCCESS + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def impResult = it.results[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == [GENERIC, OPENX, AMX, GENERIC, OPENX, AMX] + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + } + + def "PBS should remove hard alias bidder from imps when hard alias bidder excluded in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + openxAlias = Openx.defaultOpenx + } + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(OPENX_ALIAS)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [OPENX, AMX, GENERIC] + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX_ALIAS + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == SUCCESS + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def impResult = it.results[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == [OPENX_ALIAS] + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + } + + //todo: Soft alias + + def "PBS should leave only soft alias bidder at imps when soft alias bidder include in account config"() { + given: "Bid request with multiply imps bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + alias = new Generic() + generic = null + amx = new Amx() + openx = Openx.defaultOpenx + } + ext.prebid.aliases = [(ALIAS.value): GENERIC] + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(ALIAS)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seat" + assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED + assert bidResponse.seatbid.seat == [ALIAS] + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + def seatNonBid = bidResponse.ext.seatnonbid + assert seatNonBid.seat == [GENERIC, OPENX, AMX] + assert seatNonBid.nonBid[0].impId == [bidRequest.imp[0].id] + assert seatNonBid.nonBid[0].statusCode == [REQUEST_BIDDER_REMOVED_BY_MODULE] + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == SUCCESS + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def impResult = it.results[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == [GENERIC, OPENX, AMX, OPENX, AMX] + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + } + + def "PBS should remove soft alias bidder from imps when soft alias bidder excluded in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(Imp.defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + alias = new Generic() + generic = null + amx = new Amx() + openx = Openx.defaultOpenx + } + ext.prebid.aliases = [(ALIAS.value): GENERIC] + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(ALIAS)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [OPENX, AMX, GENERIC] + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == ALIAS + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == SUCCESS + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def impResult = it.results[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == [ALIAS] + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + } + + def "PBS should remove bidder and update analytics when first rule sets disabled and second enabled in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets.first.enabled = false + it.ruleSets.add(RuleSets.createRuleSets()) + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain two seat" + assert bidResponse.seatbid.size() == 2 + + and: "Response should contain seat bid" + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == SUCCESS + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def impResult = it.results[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + } + + def "PBS should skip rule set and take next one when rule sets not a processed auction request"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules engine and several rule sets" + def pbRulesEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC), + createRuleEngineModelRuleWithExcludeResult(AMX), + createRuleEngineModelRuleWithExcludeResult(OPENX)] + it.ruleSets[0].stage = stage as Stage + it.ruleSets.add(RuleSets.createRuleSets()) + it.ruleSets[1].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(AMX)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRulesEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain two seat" + assert bidResponse.seatbid.size() == 2 + + and: "Response should contain seat bid" + assert bidResponse.seatbid.seat == [GENERIC, OPENX] + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRulesEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + + where: + stage << Stage.values() - Stage.PROCESSED_AUCTION_REQUEST + } + + //todo: if synced with exclude + + def "PBS shouldn't remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=false in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(Imp.defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + } + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args[0].ifSyncedId = false + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cookies headers" + def uidsCookie = UidsCookie.defaultUidsCookie + def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about rule engine" + assert getAnalyticResults(bidResponse).isEmpty() + } + + def "PBS should remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(Imp.defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + } + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args[0].ifSyncedId = true + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cookies headers" + def uidsCookie = UidsCookie.defaultUidsCookie + def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + + def "PBS shouldn't remove bidder from imps when bidder hasn't ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + } + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args[0].ifSyncedId = true + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + } + + //todo: if synced with include + + def "PBS should leave request bidder at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=false in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(Imp.defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + } + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args[0].ifSyncedId = false + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cookies headers" + def uidsCookie = UidsCookie.defaultUidsCookie + def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about rule engine" + assert getAnalyticResults(bidResponse).isEmpty() + } + + def "PBS should leave only include bidder at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=true in account config"() { + given: "Bid request with multiply imps bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + amx = new Amx() + openx = Openx.defaultOpenx + } + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args[0].ifSyncedId = true + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cookies headers" + def uidsCookie = UidsCookie.defaultUidsCookie + def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) + + then: "Bid response should contain seat" + assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC] + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + def seatNonBid = bidResponse.ext.seatnonbid + assert seatNonBid.seat == [OPENX, AMX] + assert seatNonBid.nonBid[0].impId == [bidRequest.imp[0].id] + assert seatNonBid.nonBid[0].statusCode == [REQUEST_BIDDER_REMOVED_BY_MODULE] + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == SUCCESS + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def impResult = it.results[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == [OPENX, AMX, OPENX, AMX] + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + } + + def "PBS should leave request bidder at imps when bidder hasn't ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + } + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args[0].ifSyncedId = true + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + } + + //todo: add test with weight to be when 0 and 100 + + def "PBS should take rule with higher weight and remove bidder when two model group with different weight"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(Imp.defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + } + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with few model group" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + it.weight = 0 + it.rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] + } + it.ruleSets[0].modelGroups.add(RulesEngineModelGroups.createRulesModuleGroup()) + it.ruleSets[0].modelGroups[1].tap { + it.weight = 100 + it.rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] + } + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + } + + //todo Default leaf + + def "PBS should log the default model group and shouldn't modify response when other rules don't fire"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.device = new Device(geo: new Geo(country: BULGARIA)) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with default model" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [schema] + it.ruleSets[0].modelGroups[0].rules = [rule] + it.ruleSets[0].modelGroups[0].modelDefault = [new RuleEngineModelDefault( + function: LOG_A_TAG, + args: new RuleEngineModelDefaultArgs(analyticsValue: PBSUtils.randomString))] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == SUCCESS + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def impResult = it.results[0] + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == [LOG_A_TAG.toString()] + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.appliedTo.impIds == [WILDCARD] + } + + where: + schema | rule + createDeviceCountryInSchema() | null + null | createRuleEngineModelRule() + createDeviceCountryInSchema() | createRuleEngineModelRule() + null | null + } + + //todo: Validation with schema function + + def "PBS should reject processing rule engine when #function schema function contain args"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = function + it.args = RuleEngineFunctionArgs.defaultFunctionArgs + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + + and: "Analytics result should contain info about rule engine" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == ERROR + def impResult = it.results[0] + assert impResult.status == ERROR + assert impResult.values.message == "Function ${function} configuration is invalid: No arguments allowed" + } + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + where: + function << [DEVICE_TYPE, AD_UNIT_CODE, BUNDLE, DOMAIN, TCF_IN_SCOPE, GPP_SID_AVAILABLE, FPD_AVAILABLE, + USER_FPD_AVAILABLE, PERCENT, EID_AVAILABLE, CHANNEL, DATA_CENTER, DEVICE_COUNTRY] + } + + //todo: device country + + def "PBS should exclude bidder when deviceCountry match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema[0].function = DEVICE_COUNTRY + rules[0].conditions = [USA.toString()] + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED - 1 + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + } + + def "PBS shouldn't exclude bidder when deviceCountry not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema[0].function = DEVICE_COUNTRY + rules[0].conditions = [PBSUtils.getRandomEnum(Country, [USA]).toString()] + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + } + + //todo: INPROGRESS dataCenter don't know from where it resolve + + def "PBS should reject processing rule engine when dataCenterIn schema function args contain invalid data"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DATA_CENTER_IN + it.args = new RuleEngineFunctionArgs(datacenters: [PBSUtils.randomNumber]) + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + + and: "Analytics result should contain info about rule engine" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == ERROR + def impResult = it.results[0] + assert impResult.status == ERROR + assert impResult.values.message == "Function ${DATA_CENTER_IN} configuration is invalid: No arguments allowed" + } + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + } + + //todo: Channel + + def "PBS should exclude bidder when channel match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema[0].function = CHANNEL + rules[0].conditions = [WEB.value] + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + + def "PBS shouldn't exclude bidder when channel not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema[0].function = CHANNEL + rules[0].conditions = [PBSUtils.getRandomEnum(ChannelType, [WEB]).value] + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + } + + //todo: eidAvailable + + def "PBS should exclude bidder when eidAvailable match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + user = new User(eids: [Eid.getDefaultEid()]) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: EID_AVAILABLE)] + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + + def "PBS shouldn't exclude bidder when eidAvailable not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + user = new User(eids: eids) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema[0].function = EID_AVAILABLE + rules[0].conditions = [TRUE as String] + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + + where: + eids << [null, []] + } + + //todo: EID_IN + + def "PBS should reject processing rule engine when eidIn schema function args contain invalid data"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = EID_IN + it.args = new RuleEngineFunctionArgs(sources: [PBSUtils.randomNumber]) + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + + and: "Analytics result should contain info about rule engine" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == ERROR + def impResult = it.results[0] + assert impResult.status == ERROR + assert impResult.values.message == "Function ${EID_IN} configuration is invalid: No arguments allowed" + } + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + } + + def "PBS should exclude bidder when eidIn match with condition"() { + given: "Bid request with multiply bidders" + def eid = Eid.getDefaultEid() + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + user = new User(eids: [eid]) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DEVICE_COUNTRY_IN + it.args = new RuleEngineFunctionArgs(sources: [PBSUtils.randomString, eid.source, PBSUtils.randomString]) + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + + //todo: userUfpData + + def "PBS should exclude bidder when userFpdAvailable match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + user = requestedUfpUser + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: USER_FPD_AVAILABLE)] + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + + where: + requestedUfpUser << [new User(data: [Data.defaultData], ext: new UserExt(data: UserExtData.FPDUserExtData)), + new User(ext: new UserExt(data: UserExtData.FPDUserExtData)), + new User(data: [Data.defaultData])] + } + + def "PBS shouldn't exclude bidder when userFpdAvailable not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + user = new User(data: null, ext: new UserExt(data: null)) + + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: USER_FPD_AVAILABLE)] + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + } + + //todo: fpdAvailable + + def "PBS should exclude bidder when fpdAvailable match with condition"() { + given: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: FPD_AVAILABLE)] + } + + and: "Account with rule enigne config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + + where: + bidRequest << [ + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + user = new User(data: [Data.defaultData]) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + user = new User(ext: new UserExt(data: UserExtData.FPDUserExtData)) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + site.content = new Content(data: [Data.defaultData]) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + site.ext = new SiteExt(data: SiteExtData.FPDSiteExtData) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + app.content = new Content(data: [Data.defaultData]) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + app.ext = new AppExt(data: new AppExtData(language: PBSUtils.randomString)) + }, + ] + } + + def "PBS shouldn't exclude bidder when fpdAvailable not match with condition"() { + given: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: FPD_AVAILABLE)] + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + + where: + bidRequest << [ + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + user = new User(data: [null]) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + user = new User(ext: new UserExt(data: null)) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + site.content = new Content(data: [null]) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + site.ext = new SiteExt(data: null) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + app.content = new Content(data: [null]) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + app.ext = new AppExt(data: null) + }, + ] + } + + //todo: gppSidAvailable + + def "PBS should exclude bidder when gppSidAvailable match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + regs = new Regs(gppSid: [PBSUtils.getRandomEnum(GppSectionId).getIntValue()]) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: GPP_SID_AVAILABLE)] + } + + and: "Account with rule enigne config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + + def "PBS shouldn't exclude bidder when gppSidAvailable not match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + regs = new Regs(gppSid: gppSid) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: GPP_SID_AVAILABLE)] + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + + where: + gppSid << [[PBSUtils.randomNegativeNumber], [null], null] + } + + //todo gppSidIn + + def "PBS should reject processing rule engine when gppSidIn schema function args contain invalid data"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + regs = new Regs(gppSid: [PBSUtils.getRandomEnum(GppSectionId).getIntValue()]) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = GPP_SID_IN + it.args = new RuleEngineFunctionArgs(sids: [PBSUtils.randomString]) + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + + and: "Analytics result should contain info about rule engine" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == ERROR + def impResult = it.results[0] + assert impResult.status == ERROR + assert impResult.values.message == "Function ${GPP_SID_IN} configuration is invalid: No arguments allowed" + } + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + } + + def "PBS should exclude bidder when gppSidIn match with condition"() { + given: "Default bid request with multiply bidder" + def gppSectionId = PBSUtils.getRandomEnum(GppSectionId).getIntValue() + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + regs = new Regs(gppSid: [gppSectionId]) + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = GPP_SID_IN + it.args = new RuleEngineFunctionArgs(sids: [gppSectionId]) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + + def "PBS shouldn't exclude bidder when gppSidIn not match with condition"() { + given: "Default bid request with multiply bidder" + def gppSectionId = PBSUtils.getRandomEnum(GppSectionId) + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + regs = new Regs(gppSid: [gppSectionId.getIntValue()]) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = GPP_SID_IN + it.args = new RuleEngineFunctionArgs(sids: [PBSUtils.getRandomEnum(GppSectionId, [gppSectionId]).getIntValue()]) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + } + + //todo: tcfInScope + + def "PBS should exclude bidder when tcfInScope match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + regs = new Regs(gdpr: gdpr) + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: TCF_IN_SCOPE)] + rules[0].conditions = [condition] + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + + where: + gdpr | condition + 1 | TRUE as String + 0 | FALSE as String + } + + def "PBS shouldn't exclude bidder when tcfInScope not match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + regs = new Regs(gdpr: gdpr) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: TCF_IN_SCOPE)] + rules[0].conditions = [condition] + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + + where: + gdpr | condition + 0 | TRUE as String + 1 | FALSE as String + } + + //todo: percent + + def "PBS should reject processing rule engine when percent schema function args contain invalid data"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PERCENT + it.args = new RuleEngineFunctionArgs(ptc: PBSUtils.randomString) + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + + and: "Analytics result should contain info about rule engine" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == ERROR + def impResult = it.results[0] + assert impResult.status == ERROR + assert impResult.values.message == "Function ${PERCENT} configuration is invalid: Field 'key' is required and has to be an array of string " + } + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + } + + def "PBS should exclude bidder when percent match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PERCENT + it.args = new RuleEngineFunctionArgs(ptc: percent) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + + where: + percent << [100, PBSUtils.getRandomNumber(100)] + } + + def "PBS shouldn't exclude bidder when percent not match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PERCENT + it.args = new RuleEngineFunctionArgs(ptc: percent) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + + where: + percent << [0, PBSUtils.randomNegativeNumber] + } + + //todo: prebidKey !!! CAN BE REMOVED + + def "PBS should reject processing the rule engine when the prebidKey schema function contains incompatible arguments"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PREBID_KEY + it.args = new RuleEngineFunctionArgs(key: PBSUtils.randomNumber) + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + + and: "Analytics result should contain info about rule engine" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == ERROR + def impResult = it.results[0] + assert impResult.status == ERROR + assert impResult.values.message == "Function ${PREBID_KEY} configuration is invalid: Field 'key' is required and has to be an array of string" + } + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + } + + def "PBS should exclude bidder when prebidKey match with condition"() { + given: "Default bid request with multiply bidder" + def key = PBSUtils.randomString + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + ext.prebid.kvps = new Kvps(key: key) + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PREBID_KEY + it.args = new RuleEngineFunctionArgs(key: key) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + + def "PBS shouldn't exclude bidder when prebidKey not match with condition"() { + given: "Default bid request with multiply bidder" + def key = PBSUtils.randomString + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + ext.prebid.kvps = new Kvps(key: key) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PERCENT + it.args = new RuleEngineFunctionArgs(key: PBSUtils.randomString) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + } + + //todo: domain + + def "PBS should exclude bidder when domain match with condition"() { + given: "Default bid request with multiply bidder" + def randomDomain = PBSUtils.randomString + def bidRequest = bidRequestClosure(randomDomain) + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DOMAIN)] + rules[0].conditions = [randomDomain] + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + + where: + distributionChannel | bidRequestClosure + SITE | { String domain -> + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.site.publisher = new Publisher(domain: PBSUtils.randomString) + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + } + APP | { String domain -> + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.app.publisher = new Publisher(domain: PBSUtils.randomString) + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + } + } + DOOH | { String domain -> + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.dooh.publisher = new Publisher(domain: PBSUtils.randomString) + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + } + } + } + + def "PBS shouldn't exclude bidder when domain not match with condition"() { + given: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DOMAIN)] + rules[0].conditions = [PBSUtils.randomString] + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + + where: + bidRequest << [ + getDefaultBidRequestWithMultiplyBidders().tap { + it.site.publisher = new Publisher(domain: PBSUtils.randomString) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + it.app.publisher = new Publisher(domain: PBSUtils.randomString) + }, + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + it.dooh.publisher = new Publisher(domain: PBSUtils.randomString) + }] + + } + + //todo : domainIN + + def "PBS should reject processing the rule engine when the domainIn schema function contains incompatible arguments"() { + given: "Default bid request with multiplyB bidders" + def randomDomain = PBSUtils.randomString + def bidRequest = bidRequestClosure(randomDomain) + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DOMAIN_IN + it.args = new RuleEngineFunctionArgs(domains: [PBSUtils.randomNumber]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + + and: "Analytics result should contain info about rule engine" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == ERROR + def impResult = it.results[0] + assert impResult.status == ERROR + assert impResult.values.message == "Function ${DOMAIN_IN} configuration is invalid: Field 'bundles' is required and has to be an array of string" + } + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + where: + distributionChannel | bidRequestClosure + SITE | { String domain -> + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.site.publisher = new Publisher(domain: PBSUtils.randomString) + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + } + } + SITE | { String domain -> + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.site.domain = domain + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + } + } + APP | { String domain -> + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.app.publisher = new Publisher(domain: PBSUtils.randomString) + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + } + } + APP | { String domain -> + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.app.domain = domain + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + } + } + DOOH | { String domain -> + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.dooh.publisher = new Publisher(domain: PBSUtils.randomString) + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + } + } + DOOH | { String domain -> + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.dooh.domain = domain + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + } + } + } + + def "PBS should exclude bidder when domainIn match with condition"() { + given: "Default bid request with multiply bidder" + def randomDomain = PBSUtils.randomString + def bidRequest = bidRequestClosure(randomDomain) + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DOMAIN_IN + it.args = new RuleEngineFunctionArgs(domains: [PBSUtils.randomString, randomDomain]) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + + where: + distributionChannel | bidRequestClosure + SITE | { String domain -> + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.site.publisher = new Publisher(domain: PBSUtils.randomString) + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + } + } + SITE | { String domain -> + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.site.domain = domain + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + } + } + APP | { String domain -> + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.app.publisher = new Publisher(domain: PBSUtils.randomString) + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + } + } + APP | { String domain -> + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.app.domain = domain + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + } + } + DOOH | { String domain -> + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.dooh.publisher = new Publisher(domain: PBSUtils.randomString) + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + } + } + DOOH | { String domain -> + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.dooh.domain = domain + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + } + } + } + + def "PBS shouldn't exclude bidder when domainIn not match with condition"() { + given: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DOMAIN_IN + it.args = new RuleEngineFunctionArgs(domains: [PBSUtils.randomString, PBSUtils.randomString]) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + + where: + bidRequest << [ + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + it.site.publisher = new Publisher(domain: PBSUtils.randomString) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + it.site.domain = PBSUtils.randomString + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + it.app.publisher = new Publisher(domain: PBSUtils.randomString) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + it.app.domain = PBSUtils.randomString + }, + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + it.dooh.publisher = new Publisher(domain: PBSUtils.randomString) + }, + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + it.dooh.domain = PBSUtils.randomString + }] + + } + + //todo: Bundle + + def "PBS should exclude bidder when bundle match with condition"() { + given: "Default bid request with multiply bidder" + def bundle = PBSUtils.randomString + def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + app.bundle = bundle + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: BUNDLE)] + rules[0].conditions = [bundle] + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + + def "PBS shouldn't exclude bidder when bundle not match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + app.bundle = PBSUtils.randomString + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: BUNDLE)] + rules[0].conditions = [PBSUtils.randomString] + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + } + + //todo: BundleIn + + def "PBS should reject processing the rule engine when the bundleIn schema function contains incompatible arguments"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + app.bundle = PBSUtils.randomString + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = BUNDLE_IN + it.args = new RuleEngineFunctionArgs(bundles: [PBSUtils.randomNumber]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + + and: "Analytics result should contain info about rule engine" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == ERROR + def impResult = it.results[0] + assert impResult.status == ERROR + assert impResult.values.message == "Function ${BUNDLE_IN} configuration is invalid: Field 'bundles' is required and has to be an array of string" + } + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + } + + def "PBS should exclude bidder when bundleIn match with condition"() { + given: "Default bid request with multiply bidders" + def bundle = PBSUtils.randomString + def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + app.bundle = bundle + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0] = new RuleEngineModelSchema(function: BUNDLE_IN) + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == + REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + + def "PBS shouldn't exclude bidder when bundleIn not match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + app.bundle = PBSUtils.randomString + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = BUNDLE_IN + it.args = new RuleEngineFunctionArgs(bundles: [PBSUtils.randomString, PBSUtils.randomString]) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + } + + //todo: mediaTypeIn + + def "PBS should reject processing the rule engine when the mediaTypeIn schema function contains incompatible arguments"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = MEDIA_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: [mediaTypeInArgs]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + + and: "Analytics result should contain info about rule engine" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == ERROR + def impResult = it.results[0] + assert impResult.status == ERROR + assert impResult.values.message == "Function ${MEDIA_TYPE_IN} configuration is invalid: Field 'types' is required and has to be an array of string" + } + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + where: + mediaTypeInArgs << [null, PBSUtils.randomNumber] + } + + def "PBS should exclude bidder when mediaTypeIn match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + it.imp.add(getDefaultImpression(VIDEO)) + it.imp[1].ext.prebid.bidder.amx = new Amx() + it.imp[1].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[1].ext.prebid.bidder.generic = new Generic() + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = MEDIA_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: [VIDEO.value]) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + + def "PBS shouldn't exclude bidder when mediaTypeIn not match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = MEDIA_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: [PBSUtils.getRandomEnum(MediaType, [BANNER])]) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + } + + //todo: adUnitCode + + def "PBS should exclude bidder when adUnitCode match with condition"() { + given: "Default bid request with multiply bidders" + def adUnitCode = PBSUtils.randomString + def bidRequest = bidRequestClosure(adUnitCode) + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: AD_UNIT_CODE)] + rules[0].conditions = [adUnitCode] + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + + where: + bidRequestClosure << [ + { tagId -> + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + imp.add(defaultImpression) + imp[1].tagId = tagId + } + }, + { gpid -> + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + imp.add(defaultImpression) + imp[1].ext = new ImpExt(gpid: gpid) + } + }, + { pbAdSlot -> + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + imp.add(defaultImpression) + imp[1].ext = new ImpExt(data: new ImpExtContextData(pbAdSlot: pbAdSlot)) + } + }, + { storedRequestId -> + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + imp.add(defaultImpression) + imp[1].ext = new ImpExt(prebid: new ImpExtPrebid(storedRequest: new PrebidStoredRequest(id: storedRequestId))) + } + } + ] + } + + def "PBS shouldn't exclude bidder when adUnitCode not match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0] = new RuleEngineModelSchema(function: AD_UNIT_CODE) + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + } + + //todo: adUnitCodeIn + + def "PBS should reject processing the rule engine when the adUnitCodeIn schema function contains incompatible arguments"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + imp[0].tagId = PBSUtils.randomString + imp[0].ext = new ImpExt(gpid: PBSUtils.randomString) + imp[0].ext = new ImpExt(data: new ImpExtContextData(pbAdSlot: PBSUtils.randomString)) + imp[0].ext = new ImpExt(prebid: new ImpExtPrebid(storedRequest: new PrebidStoredRequest(id: PBSUtils.randomString))) + } + + and: "Save storedImp into DB" + def storedImp = StoredImp.getStoredImp(bidRequest).tap { + impData = getDefaultImpression() + } + storedImpDao.save(storedImp) + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = AD_UNIT_CODE + it.args = new RuleEngineFunctionArgs(codes: [arguments]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + + and: "Analytics result should contain info about rule engine" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == ERROR + def impResult = it.results[0] + assert impResult.status == ERROR + assert impResult.values.message == "Function ${AD_UNIT_CODE} configuration is invalid: Field 'type' is required and has to be an array of string" + } + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + where: + arguments << [PBSUtils.randomBoolean, PBSUtils.randomNumber] + } + + def "PBS should exclude bidder when adUnitCodeIn match with condition"() { + given: "Default bid request with multiply bidders" + def randomString = PBSUtils.randomString + def bidRequest = bidRequestClosure(randomString) + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = AD_UNIT_CODE_IN + it.args = new RuleEngineFunctionArgs(codes: [PBSUtils.randomString, randomString]) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + + where: + bidRequestClosure << [ + { storedRequestId -> + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + imp[0].tagId = PBSUtils.randomString + imp[0].ext = new ImpExt(gpid: PBSUtils.randomString) + imp[0].ext = new ImpExt(data: new ImpExtContextData(pbAdSlot: PBSUtils.randomString)) + imp[0].ext = new ImpExt(prebid: new ImpExtPrebid(storedRequest: new PrebidStoredRequest(id: storedRequestId))) + } + }, + { pbAdSlot -> + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + imp[0].tagId = PBSUtils.randomString + imp[0].ext = new ImpExt(gpid: PBSUtils.randomString) + imp[0].ext = new ImpExt(data: new ImpExtContextData(pbAdSlot: pbAdSlot)) + imp[0].ext = new ImpExt(prebid: new ImpExtPrebid(storedRequest: new PrebidStoredRequest(id: PBSUtils.randomString))) + } + }, + { gpid -> + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + imp[0].tagId = PBSUtils.randomString + imp[0].ext = new ImpExt(gpid: gpid) + imp[0].ext = new ImpExt(data: new ImpExtContextData(pbAdSlot: PBSUtils.randomString)) + imp[0].ext = new ImpExt(prebid: new ImpExtPrebid(storedRequest: new PrebidStoredRequest(id: PBSUtils.randomString))) + } + }, + { tagId -> + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + imp[0].tagId = tagId + imp[0].ext = new ImpExt(gpid: PBSUtils.randomString) + imp[0].ext = new ImpExt(data: new ImpExtContextData(pbAdSlot: PBSUtils.randomString)) + imp[0].ext = new ImpExt(prebid: new ImpExtPrebid(storedRequest: new PrebidStoredRequest(id: PBSUtils.randomString))) + } + } + ] + } + + def "PBS shouldn't exclude bidder when adUnitCodeIn not match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = AD_UNIT_CODE_IN + it.args = new RuleEngineFunctionArgs(codes: [PBSUtils.randomString, PBSUtils.randomString]) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + } + + //todo: deviceType + + def "PBS should exclude bidder when deviceType match with condition"() { + given: "Default bid request with multiply bidders" + def deviceType = PBSUtils.getRandomEnum(DeviceType) + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + device = new Device(devicetype: deviceType) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DEVICE_TYPE)] + rules[0].conditions = [deviceType.value as String] + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + + def "PBS shouldn't exclude bidder when deviceType not match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + device = new Device(devicetype: PBSUtils.getRandomEnum(DeviceType)) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DEVICE_TYPE)] + rules[0].conditions = [PBSUtils.getRandomEnum(DeviceType).value as String] + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + } + + //todo: deviceTypeIn + + def "PBS should reject processing the rule engine when the deviceTypeIn schema function contains incompatible arguments"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + device = new Device(devicetype: PBSUtils.getRandomEnum(DeviceType)) + } + + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DEVICE_COUNTRY_IN + it.args = new RuleEngineFunctionArgs(types: [PBSUtils.randomString]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + + and: "Analytics result should contain info about rule engine" + def analyticsResult = getAnalyticResults(bidResponse) + verifyAll(analyticsResult.first) { + assert it.name == RULE_ENGINE.code + assert it.status == ERROR + def impResult = it.results[0] + assert impResult.status == ERROR + assert impResult.values.message == "Function ${DEVICE_COUNTRY_IN} configuration is invalid: Field 'types' is required and has to be an array of string" + } + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + } + + def "PBS should exclude bidder when deviceTypeIn match with condition"() { + given: "Default bid request with multiply bidders" + def deviceType = PBSUtils.getRandomEnum(DeviceType) + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + device = new Device(devicetype: deviceType) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DEVICE_TYPE_IN + it.args = new RuleEngineFunctionArgs(codes: [deviceType.value]) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + } + + def "PBS shouldn't exclude bidder when deviceTypeIn not match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + device = new Device(devicetype: PBSUtils.getRandomEnum(DeviceType)) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DEVICE_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: [PBSUtils.getRandomEnum(DeviceType).value as String]) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert getAnalyticResults(bidResponse).isEmpty() + } + + + private static BidRequest getDefaultBidRequestWithMultiplyBidders(DistributionChannel distributionChannel = SITE) { + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + } + } + + private static void updateBidRequestWithGeoCountry(BidRequest bidRequest) { + bidRequest.device = new Device(geo: new Geo(country: USA)) + } + + private static void updateBidRequestWithTraceVerboseAndReturnAllBidStatus(BidRequest bidRequest) { + bidRequest.ext.prebid.tap { + it.trace = VERBOSE + it.returnAllBidStatus = true + } + } + + private static getAccountWithRulesEngine(String accountId, PbRulesEngine ruleEngine) { + def accountHooksConfiguration = new AccountHooksConfiguration(modules: new PbsModulesConfig(ruleEngine: ruleEngine)) + new Account(uuid: accountId, config: new AccountConfig(hooks: accountHooksConfiguration)) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/richmedia/RichMediaFilterSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/richmedia/RichMediaFilterSpec.groovy index 7c6e90d263e..9bcc6b7d62a 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/richmedia/RichMediaFilterSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/richmedia/RichMediaFilterSpec.groovy @@ -29,7 +29,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { private static final String PATTERN_NAME_ACCOUNT = PBSUtils.randomString private static final Map DISABLED_FILTER_SPECIFIC_PATTERN_NAME_CONFIG = getRichMediaFilterSettings(PATTERN_NAME, false) private static final Map SPECIFIC_PATTERN_NAME_CONFIG = getRichMediaFilterSettings(PATTERN_NAME) - private static final Map SNAKE_SPECIFIC_PATTERN_NAME_CONFIG = (getRichMediaFilterSettings(PATTERN_NAME) + + private static final Map SNAKE_SPECIFIC_PATTERN_NAME_CONFIG = (getRichMediaFilterSettings(PATTERN_NAME) + ["hooks.host-execution-plan": encode(ExecutionPlan.getSingleEndpointExecutionPlan(OPENRTB2_AUCTION, PB_RICHMEDIA_FILTER, [ALL_PROCESSED_BID_RESPONSES]).tap { endpoints.values().first().stages.values().first().groups.first.hookSequenceSnakeCase = [new HookId(moduleCodeSnakeCase: PB_RICHMEDIA_FILTER.code, hookImplCodeSnakeCase: "${PB_RICHMEDIA_FILTER.code}-${ALL_PROCESSED_BID_RESPONSES.value}-hook")] })]).collectEntries { key, value -> [(key.toString()): value.toString()] } @@ -155,7 +155,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_INVALID_CREATIVE @@ -239,7 +239,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_INVALID_CREATIVE @@ -323,7 +323,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_INVALID_CREATIVE @@ -440,7 +440,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_INVALID_CREATIVE @@ -454,7 +454,7 @@ class RichMediaFilterSpec extends ModuleBaseSpec { admValue << [PATTERN_NAME, "${PBSUtils.randomString}-${PATTERN_NAME}", "${PATTERN_NAME}.${PBSUtils.randomString}"] } - private static List getAnalyticResults(BidResponse response) { + static List getAnalyticResults(BidResponse response) { response.ext.prebid.modules?.trace?.stages?.first() ?.outcomes?.first()?.groups?.first() ?.invocationResults?.first()?.analyticsTags?.activities diff --git a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsRulesSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsRulesSpec.groovy index 30acd9b4bdc..cea3bca8504 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsRulesSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/pricefloors/PriceFloorsRulesSpec.groovy @@ -962,7 +962,7 @@ class PriceFloorsRulesSpec extends PriceFloorsBaseSpec { assert seatNonBids.size() == 1 def seatNonBid = seatNonBids[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_DUE_TO_PRICE_FLOOR assert seatNonBid.nonBid.size() == bidResponse.seatbid[0].bid.size() @@ -1209,7 +1209,7 @@ class PriceFloorsRulesSpec extends PriceFloorsBaseSpec { assert seatNonBids.size() == 1 def seatNonBid = seatNonBids[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_DUE_TO_PRICE_FLOOR assert seatNonBid.nonBid.size() == bidResponse.seatbid[0].bid.size() diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/DsaSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/DsaSpec.groovy index 2575789049d..c2bf06fe50a 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/DsaSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/DsaSpec.groovy @@ -1,5 +1,6 @@ package org.prebid.server.functional.tests.privacy +import org.prebid.server.functional.model.bidder.BidderName import org.prebid.server.functional.model.config.AccountDsaConfig import org.prebid.server.functional.model.db.StoredRequest import org.prebid.server.functional.model.request.amp.AmpRequest @@ -14,6 +15,7 @@ import org.prebid.server.functional.model.response.auction.DsaResponse as BidDsa import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.TcfConsent + import static org.prebid.server.functional.model.request.auction.DsaPubRender.PUB_CANT_RENDER import static org.prebid.server.functional.model.request.auction.DsaPubRender.PUB_WILL_RENDER import static org.prebid.server.functional.model.request.auction.DsaRequired.NOT_REQUIRED @@ -315,7 +317,7 @@ class DsaSpec extends PrivacyBaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_DUE_TO_DSA @@ -495,7 +497,7 @@ class DsaSpec extends PrivacyBaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_DUE_TO_DSA @@ -535,7 +537,7 @@ class DsaSpec extends PrivacyBaseSpec { assert response.ext.seatnonbid.size() == 1 def seatNonBid = response.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == BidderName.GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_DUE_TO_DSA diff --git a/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAuctionSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAuctionSpec.groovy index 5fc0f5d7bca..299d911a398 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAuctionSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAuctionSpec.groovy @@ -267,7 +267,7 @@ class GdprAuctionSpec extends PrivacyBaseSpec { assert seatNonBids.size() == 1 def seatNonBid = seatNonBids[0] - assert seatNonBid.seat == GENERIC.value + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BLOCKED_PRIVACY From 63767ef3998dd91fdff6a00f19ac3c17e1f7a4f5 Mon Sep 17 00:00:00 2001 From: markiian Date: Mon, 21 Jul 2025 15:44:23 +0300 Subject: [PATCH 03/18] Initial commit of rule engine functional test --- .../model/response/auction/ModuleValue.groovy | 20 +- .../module/pbruleengine/RuleEngineSpec.groovy | 350 ++++++++++++------ 2 files changed, 242 insertions(+), 128 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy index 48f51ed81a1..986bf8dafd9 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy @@ -15,21 +15,21 @@ class ModuleValue { ModuleName module String richmediaFormat - @JsonProperty("analytics_key") + @JsonProperty("analytics_key") String analyticsKey - @JsonProperty("analytics_value") + @JsonProperty("analytics_value") String analyticsValue - @JsonProperty("model_version") + @JsonProperty("model_version") String modelVersion - @JsonProperty("condition_fired") + @JsonProperty("condition_fired") List conditionFired - @JsonProperty("rule_fired") - String rule_fired - @JsonProperty("result_functions") - List resultFunctions - @JsonProperty("bidders_removed") + @JsonProperty("rule_fired") + String rule_fired + @JsonProperty("result_function") + String resultFunctions + @JsonProperty("bidders_removed") List biddersRemoved - @JsonProperty("seatnonbid") + @JsonProperty("seat_non_bid") String seatNonBid String message } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy index 8a6a93a43b9..ed590d0826a 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy @@ -4,6 +4,7 @@ import org.prebid.server.functional.model.ChannelType import org.prebid.server.functional.model.UidsCookie import org.prebid.server.functional.model.bidder.Generic import org.prebid.server.functional.model.bidder.Openx +import org.prebid.server.functional.model.bidderspecific.BidderRequest import org.prebid.server.functional.model.config.AccountConfig import org.prebid.server.functional.model.config.AccountHooksConfiguration import org.prebid.server.functional.model.config.PbRulesEngine @@ -51,6 +52,7 @@ import org.prebid.server.functional.tests.module.ModuleBaseSpec import org.prebid.server.functional.util.HttpUtil import org.prebid.server.functional.util.PBSUtils import spock.lang.IgnoreRest +import spock.lang.RepeatUntilFailure import static java.lang.Boolean.FALSE import static java.lang.Boolean.TRUE @@ -157,6 +159,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), pbRulesEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -175,7 +180,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "Analytics result shouldn't contain info about rule engine" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) where: pbRulesEngine << [ @@ -200,8 +205,11 @@ class RuleEngineSpec extends ModuleBaseSpec { createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results[0].args = null }) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.withWarmup().sendAuctionRequest(bidRequest) + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -218,10 +226,9 @@ class RuleEngineSpec extends ModuleBaseSpec { assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "Invocation result should contain warning of rule engine" - assert getInvocationResult(bidResponse)[0].message == "Function 'excludeBidders' configuration is invalid: Configuration is required, but not provided" + assert getInvocationResult(bidResponse)[0].message == "Rule for account ${bidRequest.accountId} is not ready" } - def "PBS shouldn't remove bidder and emit a warning when model group rule engine not fully configured in account"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -231,9 +238,12 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with enabled rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), - createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups = [] },) + createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups = [] }) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -252,11 +262,10 @@ class RuleEngineSpec extends ModuleBaseSpec { assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "Invocation result should contain warning of rule engine" - assert getInvocationResult(bidResponse)[0].message == "Weighted list cannot be empty" + assert getInvocationResult(bidResponse)[0].message == "Rule for account ${bidRequest.accountId} is not ready" } //todo what should happened when updateBidRequestWithTraceVerboseAndReturnAllBidStatus absent and validation failed - def "PBS should logAtag when default model doesn't exist"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -271,6 +280,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cahce account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -304,7 +316,6 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - //todo : need to fix from dev def "PBS shouldn't remove bidder when rule engine disabled or absent in account"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -326,7 +337,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert bidResponse.seatbid.size() == BIDDERS_REQUESTED and: "Analytics result shouldn't contain info about rule engine" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid @@ -363,7 +374,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert bidResponse.seatbid.size() == BIDDERS_REQUESTED and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -373,7 +384,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid } - def "PBS should remove bidder and not update analytics when bidder matched with conditions and without analytics value"() { + def "PBS should remove bidder and not update analytics when bidder matched with conditions and without analytics key"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) @@ -382,11 +393,14 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rules sets without analytics value" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results[0].args.analyticsValue = null + it.ruleSets[0].modelGroups[0].analyticsKey = null } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -394,13 +408,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 and: "Response should contain seat bid" - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -434,14 +448,17 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.seat == [OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == 2 + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -494,14 +511,17 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.seat == [OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == 2 + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -556,6 +576,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -563,7 +586,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == 2 + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -591,7 +614,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - @IgnoreRest + //todo UNKNONW BIDDER ADDED def "PBS shouldn't remove bidder and don't update analytics when bidder doesn't specified with account and request"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -607,6 +630,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -627,7 +653,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert getInvocationResult(bidResponse)[0].action == ResponseAction.NO_INVOCATION } - @IgnoreRest def "PBS should include one bidder and update analytics when multiple bidders specified and one included in account"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -642,6 +667,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -661,9 +689,9 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-filter" assert it.status == SUCCESS - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] def impResult = it.results[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey @@ -673,7 +701,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] assert impResult.values.biddersRemoved == [GENERIC, AMX] assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.appliedTo.impIds == ["*"] } } @@ -696,6 +724,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -757,14 +788,15 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED - - and: "Bid response should contain seatBid.bids" - assert bidResponse.seatbid[0].bid.size() == 2 + assert bidResponse.seatbid.seat == [GENERIC] and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -789,7 +821,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue assert impResult.values.conditionFired == groups.rules.first.conditions assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == [OPENX, AMX, OPENX, AMX] + assert impResult.values.biddersRemoved == [OPENX, AMX] assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id } @@ -814,6 +846,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -869,14 +904,17 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED - and: "Bid response should contain seatBid.bids" - assert bidResponse.seatbid[0].bid.size() == 1 + and: "Bid response should contain seatBid.seat" + assert bidResponse.seatbid.seat == [OPENX_ALIAS] and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -930,6 +968,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -994,6 +1035,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -1055,6 +1099,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -1110,6 +1157,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -1171,6 +1221,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRulesEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -1178,7 +1231,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert bidResponse.seatbid.size() == 2 and: "Response should contain seat bid" - assert bidResponse.seatbid.seat == [GENERIC, OPENX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX].sort() and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -1217,14 +1270,9 @@ class RuleEngineSpec extends ModuleBaseSpec { //todo: if synced with exclude - def "PBS shouldn't remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=false in account config"() { + def "PBS shouldn't remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(Imp.defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - } updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } @@ -1232,7 +1280,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args[0].ifSyncedId = false + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = true } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -1241,15 +1289,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def uidsCookie = UidsCookie.defaultUidsCookie def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -1259,17 +1310,12 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about rule engine" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) } - def "PBS should remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { + def "PBS should remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=false in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(Imp.defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - } updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } @@ -1277,7 +1323,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args[0].ifSyncedId = true + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = false } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -1286,15 +1332,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def uidsCookie = UidsCookie.defaultUidsCookie def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat == [OPENX, AMX] and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -1328,11 +1377,6 @@ class RuleEngineSpec extends ModuleBaseSpec { def "PBS shouldn't remove bidder from imps when bidder hasn't ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - } updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } @@ -1340,20 +1384,23 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args[0].ifSyncedId = true + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = true } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -1363,19 +1410,14 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) } //todo: if synced with include - def "PBS should leave request bidder at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=false in account config"() { + def "PBS should leave request bidder at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=true in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(Imp.defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - } updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } @@ -1383,7 +1425,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args[0].ifSyncedId = false + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = true } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -1392,15 +1434,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def uidsCookie = UidsCookie.defaultUidsCookie def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -1410,17 +1455,12 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about rule engine" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) } - def "PBS should leave only include bidder at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=true in account config"() { + def "PBS should leave only include bidder at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=false in account config"() { given: "Bid request with multiply imps bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - amx = new Amx() - openx = Openx.defaultOpenx - } updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } @@ -1428,7 +1468,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args[0].ifSyncedId = true + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = false } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -1437,6 +1477,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def uidsCookie = UidsCookie.defaultUidsCookie def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) @@ -1470,7 +1513,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue assert impResult.values.conditionFired == groups.rules.first.conditions assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == [OPENX, AMX, OPENX, AMX] + assert impResult.values.biddersRemoved == [OPENX, AMX] assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id } @@ -1479,11 +1522,6 @@ class RuleEngineSpec extends ModuleBaseSpec { def "PBS should leave request bidder at imps when bidder hasn't ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - } updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } @@ -1491,7 +1529,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args[0].ifSyncedId = true + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = true } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -1501,7 +1539,7 @@ class RuleEngineSpec extends ModuleBaseSpec { then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED @@ -1514,19 +1552,57 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) } //todo: add test with weight to be when 0 and 100 - def "PBS should take rule with higher weight and remove bidder when two model group with different weight"() { + def "PBS shouldn't take rule with higher weight and remove bidder when weight negative or zero"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(Imp.defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with few model group" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + it.weight = weight } + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + weight << [PBSUtils.randomNegativeNumber, 0] + } + + def "PBS should take rule with higher weight and remove bidder when two model group with different weight"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } @@ -1534,7 +1610,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with few model group" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].tap { - it.weight = 0 + it.weight = 1 it.rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] } it.ruleSets[0].modelGroups.add(RulesEngineModelGroups.createRulesModuleGroup()) @@ -1546,28 +1622,49 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id } - //todo Default leaf + //todo Default leaf need confirm def "PBS should log the default model group and shouldn't modify response when other rules don't fire"() { given: "Bid request with multiply bidders" @@ -1578,8 +1675,6 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with default model" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema = [schema] - it.ruleSets[0].modelGroups[0].rules = [rule] it.ruleSets[0].modelGroups[0].modelDefault = [new RuleEngineModelDefault( function: LOG_A_TAG, args: new RuleEngineModelDefaultArgs(analyticsValue: PBSUtils.randomString))] @@ -1587,15 +1682,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -1618,16 +1716,9 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] assert impResult.appliedTo.impIds == [WILDCARD] } - - where: - schema | rule - createDeviceCountryInSchema() | null - null | createRuleEngineModelRule() - createDeviceCountryInSchema() | createRuleEngineModelRule() - null | null } - //todo: Validation with schema function + //todo: Validation with schema function , SAME CURRENTLY IT's In log def "PBS should reject processing rule engine when #function schema function contain args"() { given: "Default bid request with multiply bidders" @@ -1647,6 +1738,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -1663,7 +1755,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert it.status == ERROR def impResult = it.results[0] assert impResult.status == ERROR - assert impResult.values.message == "Function ${function} configuration is invalid: No arguments allowed" } and: "PBS response shouldn't contain seatNonBid" @@ -1680,6 +1771,7 @@ class RuleEngineSpec extends ModuleBaseSpec { //todo: device country + @IgnoreRest def "PBS should exclude bidder when deviceCountry match with condition"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -1691,6 +1783,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].tap { schema[0].function = DEVICE_COUNTRY + schema[0].args = null rules[0].conditions = [USA.toString()] } } @@ -1699,25 +1792,46 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED - 1 + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == RULE_ENGINE.code + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id - and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE } def "PBS shouldn't exclude bidder when deviceCountry not match with condition"() { From ab17da1a3d551af1b6ab484b0d6f3c6428bfce28 Mon Sep 17 00:00:00 2001 From: markiian Date: Fri, 25 Jul 2025 12:37:14 +0300 Subject: [PATCH 04/18] Initial commit of rule engine functional test --- .../schema/functions/MediaTypeInFunction.java | 2 +- .../model/config/RuleEngineFunction.groovy | 4 +- .../config/RuleEngineFunctionArgs.groovy | 4 +- .../model/request/auction/Kvps.groovy | 2 +- .../module/pbruleengine/RuleEngineSpec.groovy | 1185 ++++++++++------- 5 files changed, 727 insertions(+), 470 deletions(-) diff --git a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java index 84a2432cf40..ecdead925dd 100644 --- a/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java +++ b/extra/modules/rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/schema/functions/MediaTypeInFunction.java @@ -31,7 +31,7 @@ public String extract(SchemaFunctionArguments arguments) { final BidRequest bidRequest = context.getBidRequest(); final Imp adUnit = ListUtils.emptyIfNull(bidRequest.getImp()).stream() - .filter(imp -> StringUtils.equals(impId, impId)) + .filter(imp -> StringUtils.equals(imp.getId(), impId)) .findFirst() .orElseThrow(() -> new IllegalStateException( "Critical error in rules engine. Imp id of absent imp supplied")); diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy index 3a53dfe9ebe..dea4dcb251d 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy @@ -7,12 +7,12 @@ enum RuleEngineFunction { DEVICE_COUNTRY("deviceCountry"), DEVICE_COUNTRY_IN("deviceCountryIn"), DATA_CENTER("dataCenter"), - DATA_CENTER_IN("dataCenter"), + DATA_CENTER_IN("datacenterIn"), CHANNEL("channel"), EID_AVAILABLE("eidAvailable"), EID_IN("eidIn"), USER_FPD_AVAILABLE("userFpdAvailable"), - FPD_AVAILABLE("fpdAvail"), + FPD_AVAILABLE("fpdAvailable"), GPP_SID_AVAILABLE("gppSidAvailable"), GPP_SID_IN("gppSidIn"), TCF_IN_SCOPE("tcfInScope"), diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy index edc97471c35..55110b050b0 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy @@ -8,7 +8,7 @@ class RuleEngineFunctionArgs { List datacenters List sources List sids - Object ptc + Object pct String key List domains List bundles @@ -24,7 +24,7 @@ class RuleEngineFunctionArgs { datacenters = [PBSUtils.randomString] sources = [PBSUtils.randomString] sids = [PBSUtils.randomNumber] - ptc = PBSUtils.randomString + pct = PBSUtils.randomString key = PBSUtils.randomString domains = [PBSUtils.randomString] bundles = [PBSUtils.randomString] diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Kvps.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Kvps.groovy index f1706a2b220..0565f09c72f 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/Kvps.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Kvps.groovy @@ -2,5 +2,5 @@ package org.prebid.server.functional.model.request.auction class Kvps { - String key + String anyString } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy index ed590d0826a..bab7d76b96b 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy @@ -4,7 +4,6 @@ import org.prebid.server.functional.model.ChannelType import org.prebid.server.functional.model.UidsCookie import org.prebid.server.functional.model.bidder.Generic import org.prebid.server.functional.model.bidder.Openx -import org.prebid.server.functional.model.bidderspecific.BidderRequest import org.prebid.server.functional.model.config.AccountConfig import org.prebid.server.functional.model.config.AccountHooksConfiguration import org.prebid.server.functional.model.config.PbRulesEngine @@ -46,14 +45,18 @@ 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.model.response.auction.BidRejectionReason +import org.prebid.server.functional.model.response.auction.BidResponse import org.prebid.server.functional.model.response.auction.ResponseAction import org.prebid.server.functional.service.PrebidServerService import org.prebid.server.functional.tests.module.ModuleBaseSpec import org.prebid.server.functional.util.HttpUtil import org.prebid.server.functional.util.PBSUtils +import org.prebid.server.functional.util.privacy.TcfConsent import spock.lang.IgnoreRest import spock.lang.RepeatUntilFailure +import java.time.Instant + import static java.lang.Boolean.FALSE import static java.lang.Boolean.TRUE import static org.prebid.server.functional.model.ChannelType.WEB @@ -88,11 +91,9 @@ import static org.prebid.server.functional.model.config.RuleEngineFunction.PERCE import static org.prebid.server.functional.model.config.RuleEngineFunction.PREBID_KEY import static org.prebid.server.functional.model.config.RuleEngineFunction.TCF_IN_SCOPE import static org.prebid.server.functional.model.config.RuleEngineFunction.USER_FPD_AVAILABLE -import static org.prebid.server.functional.model.config.RuleEngineModelRule.createRuleEngineModelRule import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithExcludeResult import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithIncludeResult import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithLogATagResult -import static org.prebid.server.functional.model.config.RuleEngineModelSchema.createDeviceCountryInSchema import static org.prebid.server.functional.model.pricefloors.Country.BULGARIA import static org.prebid.server.functional.model.pricefloors.Country.USA import static org.prebid.server.functional.model.pricefloors.MediaType.BANNER @@ -107,6 +108,8 @@ import static org.prebid.server.functional.model.response.auction.BidRejectionRe import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_MODULE import static org.prebid.server.functional.model.response.auction.MediaType.* import static org.prebid.server.functional.testcontainers.Dependencies.getNetworkServiceContainer +import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID +import static org.prebid.server.functional.util.privacy.TcfConsent.PurposeId.BASIC_ADS class RuleEngineSpec extends ModuleBaseSpec { @@ -119,10 +122,9 @@ class RuleEngineSpec extends ModuleBaseSpec { "adapters.${AMX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] private static final Map OPENX_ALIAS_CONFIG = ["adapters.${OPENX}.aliases.${OPENX_ALIAS}.enabled" : "true", "adapters.${OPENX}.aliases.${OPENX_ALIAS}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] - private static final String dataCenter = PBSUtils.randomString - private static final Map DATA_CENTER = ['datacenter-region': dataCenter] + private static final String DATA_CENTER = PBSUtils.randomString private static final PrebidServerService pbsServiceWithRulesEngineModule = pbsServiceFactory.getService( - getRulesEngineSettings() + AMX_CONFIG + OPENX_CONFIG + OPENX_ALIAS_CONFIG) + getRulesEngineSettings() + AMX_CONFIG + OPENX_CONFIG + OPENX_ALIAS_CONFIG + ['datacenter-region': DATA_CENTER]) //todo test cases: DONE // If 'exclude', then remove these bidders from imp[n].ext.prebid.bidders @@ -291,7 +293,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -598,9 +600,9 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-filter" assert it.status == SUCCESS - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] def impResult = it.results[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey @@ -614,7 +616,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - //todo UNKNONW BIDDER ADDED + //todo UNKNONW BIDDER ADDED - ANSWERED PLEASE PAY ATTENTION def "PBS shouldn't remove bidder and don't update analytics when bidder doesn't specified with account and request"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -637,7 +639,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.size() == 0 and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -709,10 +711,6 @@ class RuleEngineSpec extends ModuleBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { it.imp.add(defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - } updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } @@ -734,7 +732,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert bidResponse.seatbid.size() == 2 and: "Bid response should contain seatBids" - assert bidResponse.seatbid.seat == [OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -753,7 +751,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-filter" assert it.status == SUCCESS def groups = pbRuleEngine.ruleSets[1].modelGroups[0] def impResult = it.results[0] @@ -811,7 +809,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-filter" assert it.status == SUCCESS def groups = pbRuleEngine.ruleSets[1].modelGroups[0] def impResult = it.results[0] @@ -868,7 +866,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-analytics-logger" assert it.status == SUCCESS def groups = pbRuleEngine.ruleSets[1].modelGroups[0] def impResult = it.results[0] @@ -932,7 +930,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-filter" assert it.status == SUCCESS def groups = pbRuleEngine.ruleSets[1].modelGroups[0] def impResult = it.results[0] @@ -976,7 +974,7 @@ class RuleEngineSpec extends ModuleBaseSpec { then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [OPENX, AMX, GENERIC] + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX, GENERIC].sort() and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -995,7 +993,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-filter" assert it.status == SUCCESS def groups = pbRuleEngine.ruleSets[1].modelGroups[0] def impResult = it.results[0] @@ -1061,7 +1059,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-filter" assert it.status == SUCCESS def groups = pbRuleEngine.ruleSets[1].modelGroups[0] def impResult = it.results[0] @@ -1107,7 +1105,7 @@ class RuleEngineSpec extends ModuleBaseSpec { then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [OPENX, AMX, GENERIC] + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX, GENERIC].sort() and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -1126,7 +1124,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-filter" assert it.status == SUCCESS def groups = pbRuleEngine.ruleSets[1].modelGroups[0] def impResult = it.results[0] @@ -1167,7 +1165,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert bidResponse.seatbid.size() == 2 and: "Response should contain seat bid" - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -1186,7 +1184,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-filter" assert it.status == SUCCESS def groups = pbRuleEngine.ruleSets[1].modelGroups[0] def impResult = it.results[0] @@ -1250,7 +1248,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRulesEngine.ruleSets[1].modelGroups[0] @@ -1359,7 +1357,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[1].modelGroups[0] @@ -1503,7 +1501,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-filter" assert it.status == SUCCESS def groups = pbRuleEngine.ruleSets[1].modelGroups[0] def impResult = it.results[0] @@ -1542,7 +1540,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -1649,7 +1647,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[1].modelGroups[0] @@ -1705,7 +1703,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-filter" assert it.status == SUCCESS def groups = pbRuleEngine.ruleSets[1].modelGroups[0] def impResult = it.results[0] @@ -1751,7 +1749,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about rule engine" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-filter" assert it.status == ERROR def impResult = it.results[0] assert impResult.status == ERROR @@ -1771,7 +1769,6 @@ class RuleEngineSpec extends ModuleBaseSpec { //todo: device country - @IgnoreRest def "PBS should exclude bidder when deviceCountry match with condition"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -1782,8 +1779,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].tap { - schema[0].function = DEVICE_COUNTRY - schema[0].args = null + schema = [new RuleEngineModelSchema(function: DEVICE_COUNTRY)] rules[0].conditions = [USA.toString()] } } @@ -1812,7 +1808,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[1].modelGroups[0] @@ -1853,15 +1849,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Account cache" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -1871,10 +1870,10 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) } - //todo: INPROGRESS dataCenter don't know from where it resolve + //todo DataCenterIN and dataCenter def "PBS should reject processing rule engine when dataCenterIn schema function args contain invalid data"() { given: "Bid request with multiply bidders" @@ -1886,7 +1885,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { it.function = DATA_CENTER_IN - it.args = new RuleEngineFunctionArgs(datacenters: [PBSUtils.randomNumber]) + it.args = new RuleEngineFunctionArgs(countries: [DATA_CENTER]) } } @@ -1894,31 +1893,130 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - then: "PBs should perform bidder request" + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + + and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) - and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors - and: "Analytics result should contain info about rule engine" + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should exclude bidder when dataCentersIn match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DATA_CENTER_IN + it.args = new RuleEngineFunctionArgs(datacenters: [DATA_CENTER]) + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code - assert it.status == ERROR - def impResult = it.results[0] - assert impResult.status == ERROR - assert impResult.values.message == "Function ${DATA_CENTER_IN} configuration is invalid: No arguments allowed" + def result = analyticsResult[0] + assert result.name == "rules-filter" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.conditionFired == groups.rules.first.conditions + assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.appliedTo.impIds == bidRequest.imp.id + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + } + + def "PBS shouldn't exclude bidder when dataCentersIn not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DATA_CENTER_IN + it.args = new RuleEngineFunctionArgs(datacenters: [PBSUtils.randomString]) + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) } //todo: Channel @@ -1932,7 +2030,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].tap { - schema[0].function = CHANNEL + schema = [new RuleEngineModelSchema(function: CHANNEL)] rules[0].conditions = [WEB.value] } } @@ -1941,31 +2039,27 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[1].modelGroups[0] @@ -1978,6 +2072,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE } def "PBS shouldn't exclude bidder when channel not match with condition"() { @@ -1989,7 +2090,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].tap { - schema[0].function = CHANNEL + schema = [new RuleEngineModelSchema(function: CHANNEL)] rules[0].conditions = [PBSUtils.getRandomEnum(ChannelType, [WEB]).value] } } @@ -1998,15 +2099,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -2016,7 +2120,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) } //todo: eidAvailable @@ -2037,31 +2141,27 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[1].modelGroups[0] @@ -2074,6 +2174,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE } def "PBS shouldn't exclude bidder when eidAvailable not match with condition"() { @@ -2095,15 +2202,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -2113,7 +2223,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) where: eids << [null, []] @@ -2152,7 +2262,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about rule engine" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-filter" assert it.status == ERROR def impResult = it.results[0] assert impResult.status == ERROR @@ -2178,7 +2288,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = DEVICE_COUNTRY_IN + it.function = EID_IN it.args = new RuleEngineFunctionArgs(sources: [PBSUtils.randomString, eid.source, PBSUtils.randomString]) } } @@ -2187,31 +2297,27 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[1].modelGroups[0] @@ -2224,6 +2330,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE } //todo: userUfpData @@ -2244,34 +2357,30 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version @@ -2282,21 +2391,21 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id - where: - requestedUfpUser << [new User(data: [Data.defaultData], ext: new UserExt(data: UserExtData.FPDUserExtData)), - new User(ext: new UserExt(data: UserExtData.FPDUserExtData)), + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + + where: + requestedUfpUser << [new User(data: [Data.defaultData], ext: new UserExt(data: UserExtData.FPDUserExtData)), + new User(ext: new UserExt(data: UserExtData.FPDUserExtData)), new User(data: [Data.defaultData])] } def "PBS shouldn't exclude bidder when userFpdAvailable not match with condition"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - user = new User(data: null, ext: new UserExt(data: null)) - - } - - and: "Account with rule engine config" + given: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: USER_FPD_AVAILABLE)] } @@ -2305,15 +2414,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -2323,9 +2435,28 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() - } + assert !getAnalyticResults(bidResponse) + where: + bidRequest << [ + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + user = new User(data: null) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + user = new User(data: [null]) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + user = new User(ext: new UserExt(data: null)) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + user = new User(data: null, ext: new UserExt(data: null)) + } + ] + } //todo: fpdAvailable def "PBS should exclude bidder when fpdAvailable match with condition"() { @@ -2334,38 +2465,34 @@ class RuleEngineSpec extends ModuleBaseSpec { it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: FPD_AVAILABLE)] } - and: "Account with rule enigne config" + and: "Account with rule engine config" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version @@ -2376,6 +2503,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + where: bidRequest << [ getDefaultBidRequestWithMultiplyBidders().tap { @@ -2415,15 +2549,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -2433,13 +2570,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) where: bidRequest << [ getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - user = new User(data: [null]) + user = new User(data: null, ext: new UserExt(data: null)) }, getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) @@ -2448,6 +2585,7 @@ class RuleEngineSpec extends ModuleBaseSpec { getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) site.content = new Content(data: [null]) + site.ext = new SiteExt(data: null) }, getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) @@ -2456,6 +2594,7 @@ class RuleEngineSpec extends ModuleBaseSpec { getDefaultBidRequestWithMultiplyBidders(APP).tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) app.content = new Content(data: [null]) + app.ext = new AppExt(data: null) }, getDefaultBidRequestWithMultiplyBidders(APP).tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) @@ -2478,38 +2617,34 @@ class RuleEngineSpec extends ModuleBaseSpec { it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: GPP_SID_AVAILABLE)] } - and: "Account with rule enigne config" + and: "Account with rule engine config" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version @@ -2519,6 +2654,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE } def "PBS shouldn't exclude bidder when gppSidAvailable not match with condition"() { @@ -2537,15 +2679,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -2555,19 +2700,22 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) where: - gppSid << [[PBSUtils.randomNegativeNumber], [null], null] + gppSid << [[PBSUtils.randomNegativeNumber], null] } //todo gppSidIn def "PBS should reject processing rule engine when gppSidIn schema function args contain invalid data"() { - given: "Default bid request with multiply bidders" + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - regs = new Regs(gppSid: [PBSUtils.getRandomEnum(GppSectionId).getIntValue()]) + regs = new Regs(gdpr: 0, gppSid: [PBSUtils.getRandomEnum(GppSectionId, [GppSectionId.TCF_EU_V2]).getIntValue()]) } and: "Account with rule engine config" @@ -2591,22 +2739,21 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Bid response should contain all requested bidders" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - and: "Analytics result should contain info about rule engine" - def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code - assert it.status == ERROR - def impResult = it.results[0] - assert impResult.status == ERROR - assert impResult.values.message == "Function ${GPP_SID_IN} configuration is invalid: No arguments allowed" - } - and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Failed to parse rule-engine config for account ${bidRequest.accountId}: " + + "Function '${GPP_SID_IN}' configuration is invalid: " + + "Field 'sids' is required and has to be an array of integers").size() == 1 } def "PBS should exclude bidder when gppSidIn match with condition"() { @@ -2629,34 +2776,30 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version @@ -2666,6 +2809,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE } def "PBS shouldn't exclude bidder when gppSidIn not match with condition"() { @@ -2688,15 +2838,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -2706,7 +2859,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) } //todo: tcfInScope @@ -2716,6 +2869,10 @@ class RuleEngineSpec extends ModuleBaseSpec { def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) regs = new Regs(gdpr: gdpr) + user = new User(ext: new UserExt(consent: new TcfConsent.Builder() + .setPurposesLITransparency(BASIC_ADS) + .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .build())) } and: "Create rule engine config" @@ -2730,31 +2887,27 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[1].modelGroups[0] @@ -2768,6 +2921,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + where: gdpr | condition 1 | TRUE as String @@ -2779,6 +2939,10 @@ class RuleEngineSpec extends ModuleBaseSpec { def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) regs = new Regs(gdpr: gdpr) + user = new User(ext: new UserExt(consent: new TcfConsent.Builder() + .setPurposesLITransparency(BASIC_ADS) + .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .build())) } and: "Create account with rule engine config" @@ -2793,15 +2957,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Account cache" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -2811,7 +2978,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) where: gdpr | condition @@ -2831,7 +2998,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { it.function = PERCENT - it.args = new RuleEngineFunctionArgs(ptc: PBSUtils.randomString) + it.args = new RuleEngineFunctionArgs(pct: PBSUtils.randomString) } } @@ -2839,6 +3006,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -2851,7 +3021,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about rule engine" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-filter" assert it.status == ERROR def impResult = it.results[0] assert impResult.status == ERROR @@ -2876,7 +3046,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { it.function = PERCENT - it.args = new RuleEngineFunctionArgs(ptc: percent) + it.args = new RuleEngineFunctionArgs(pct: percent as Integer) } } @@ -2884,34 +3054,30 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version @@ -2922,6 +3088,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + where: percent << [100, PBSUtils.getRandomNumber(100)] } @@ -2936,7 +3109,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { it.function = PERCENT - it.args = new RuleEngineFunctionArgs(ptc: percent) + it.args = new RuleEngineFunctionArgs(pct: percent) } } @@ -2944,15 +3117,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -2962,13 +3138,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) where: percent << [0, PBSUtils.randomNegativeNumber] } - //todo: prebidKey !!! CAN BE REMOVED + //todo: prebidKey !!! CAN BE REMOVED || REINVESTIGATION AFTER UPDATE def "PBS should reject processing the rule engine when the prebidKey schema function contains incompatible arguments"() { given: "Default bid request with multiply bidders" @@ -3000,7 +3176,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about rule engine" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-filter" assert it.status == ERROR def impResult = it.results[0] assert impResult.status == ERROR @@ -3020,46 +3196,43 @@ class RuleEngineSpec extends ModuleBaseSpec { def key = PBSUtils.randomString def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - ext.prebid.kvps = new Kvps(key: key) + ext.prebid.kvps = new Kvps(anyString: key) } and: "Create rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { it.function = PREBID_KEY - it.args = new RuleEngineFunctionArgs(key: key) + it.args = new RuleEngineFunctionArgs(key: "anyString") } + it.ruleSets[0].modelGroups[0].rules[0].conditions = [key] } and: "Save account with rule engine config" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[1].modelGroups[0] @@ -3072,22 +3245,29 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE } def "PBS shouldn't exclude bidder when prebidKey not match with condition"() { given: "Default bid request with multiply bidder" - def key = PBSUtils.randomString def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - ext.prebid.kvps = new Kvps(key: key) + ext.prebid.kvps = new Kvps(anyString: PBSUtils.randomString) } - and: "Create account with rule engine config" + and: "Create rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = PERCENT - it.args = new RuleEngineFunctionArgs(key: PBSUtils.randomString) + it.function = PREBID_KEY + it.args = new RuleEngineFunctionArgs(key: "anyString") } + it.ruleSets[0].modelGroups[0].rules[0].conditions = [PBSUtils.randomString] } and: "Save account with disabled or without rules engine" @@ -3099,10 +3279,10 @@ class RuleEngineSpec extends ModuleBaseSpec { then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -3112,7 +3292,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) } //todo: domain @@ -3134,34 +3314,30 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version @@ -3172,11 +3348,18 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + where: distributionChannel | bidRequestClosure SITE | { String domain -> BidRequest.getDefaultBidRequest(distributionChannel).tap { - it.site.publisher = new Publisher(domain: PBSUtils.randomString) + it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) it.imp[0].ext.prebid.bidder.amx = new Amx() it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx it.imp[0].ext.prebid.bidder.generic = new Generic() @@ -3185,18 +3368,20 @@ class RuleEngineSpec extends ModuleBaseSpec { } APP | { String domain -> BidRequest.getDefaultBidRequest(distributionChannel).tap { - it.app.publisher = new Publisher(domain: PBSUtils.randomString) + it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) it.imp[0].ext.prebid.bidder.amx = new Amx() it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx it.imp[0].ext.prebid.bidder.generic = new Generic() + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } } DOOH | { String domain -> BidRequest.getDefaultBidRequest(distributionChannel).tap { - it.dooh.publisher = new Publisher(domain: PBSUtils.randomString) + it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) it.imp[0].ext.prebid.bidder.amx = new Amx() it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx it.imp[0].ext.prebid.bidder.generic = new Generic() + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } } } @@ -3214,15 +3399,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -3232,18 +3420,18 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) where: bidRequest << [ getDefaultBidRequestWithMultiplyBidders().tap { - it.site.publisher = new Publisher(domain: PBSUtils.randomString) + it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) }, getDefaultBidRequestWithMultiplyBidders(APP).tap { - it.app.publisher = new Publisher(domain: PBSUtils.randomString) + it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) }, getDefaultBidRequestWithMultiplyBidders(DOOH).tap { - it.dooh.publisher = new Publisher(domain: PBSUtils.randomString) + it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) }] } @@ -3279,7 +3467,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about rule engine" def analyticsResult = getAnalyticResults(bidResponse) verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code + assert it.name == "rules-filter" assert it.status == ERROR def impResult = it.results[0] assert impResult.status == ERROR @@ -3297,7 +3485,7 @@ class RuleEngineSpec extends ModuleBaseSpec { distributionChannel | bidRequestClosure SITE | { String domain -> BidRequest.getDefaultBidRequest(distributionChannel).tap { - it.site.publisher = new Publisher(domain: PBSUtils.randomString) + it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) it.imp[0].ext.prebid.bidder.amx = new Amx() it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx it.imp[0].ext.prebid.bidder.generic = new Generic() @@ -3313,7 +3501,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } APP | { String domain -> BidRequest.getDefaultBidRequest(distributionChannel).tap { - it.app.publisher = new Publisher(domain: PBSUtils.randomString) + it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) it.imp[0].ext.prebid.bidder.amx = new Amx() it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx it.imp[0].ext.prebid.bidder.generic = new Generic() @@ -3329,7 +3517,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } DOOH | { String domain -> BidRequest.getDefaultBidRequest(distributionChannel).tap { - it.dooh.publisher = new Publisher(domain: PBSUtils.randomString) + it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) it.imp[0].ext.prebid.bidder.amx = new Amx() it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx it.imp[0].ext.prebid.bidder.generic = new Generic() @@ -3362,34 +3550,30 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version @@ -3400,14 +3584,23 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + where: distributionChannel | bidRequestClosure SITE | { String domain -> BidRequest.getDefaultBidRequest(distributionChannel).tap { - it.site.publisher = new Publisher(domain: PBSUtils.randomString) + it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) it.imp[0].ext.prebid.bidder.amx = new Amx() it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx it.imp[0].ext.prebid.bidder.generic = new Generic() + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } } SITE | { String domain -> @@ -3416,14 +3609,18 @@ class RuleEngineSpec extends ModuleBaseSpec { it.imp[0].ext.prebid.bidder.amx = new Amx() it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx it.imp[0].ext.prebid.bidder.generic = new Generic() + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } } APP | { String domain -> BidRequest.getDefaultBidRequest(distributionChannel).tap { - it.app.publisher = new Publisher(domain: PBSUtils.randomString) + it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) it.imp[0].ext.prebid.bidder.amx = new Amx() it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx it.imp[0].ext.prebid.bidder.generic = new Generic() + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } } APP | { String domain -> @@ -3432,14 +3629,18 @@ class RuleEngineSpec extends ModuleBaseSpec { it.imp[0].ext.prebid.bidder.amx = new Amx() it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx it.imp[0].ext.prebid.bidder.generic = new Generic() + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } } DOOH | { String domain -> BidRequest.getDefaultBidRequest(distributionChannel).tap { - it.dooh.publisher = new Publisher(domain: PBSUtils.randomString) + it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) it.imp[0].ext.prebid.bidder.amx = new Amx() it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx it.imp[0].ext.prebid.bidder.generic = new Generic() + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } } DOOH | { String domain -> @@ -3448,6 +3649,7 @@ class RuleEngineSpec extends ModuleBaseSpec { it.imp[0].ext.prebid.bidder.amx = new Amx() it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx it.imp[0].ext.prebid.bidder.generic = new Generic() + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } } } @@ -3465,15 +3667,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -3483,13 +3688,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) where: bidRequest << [ getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - it.site.publisher = new Publisher(domain: PBSUtils.randomString) + it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) }, getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) @@ -3497,7 +3702,7 @@ class RuleEngineSpec extends ModuleBaseSpec { }, getDefaultBidRequestWithMultiplyBidders(APP).tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - it.app.publisher = new Publisher(domain: PBSUtils.randomString) + it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) }, getDefaultBidRequestWithMultiplyBidders(APP).tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) @@ -3505,7 +3710,7 @@ class RuleEngineSpec extends ModuleBaseSpec { }, getDefaultBidRequestWithMultiplyBidders(DOOH).tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - it.dooh.publisher = new Publisher(domain: PBSUtils.randomString) + it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) }, getDefaultBidRequestWithMultiplyBidders(DOOH).tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) @@ -3536,34 +3741,30 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version @@ -3573,6 +3774,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE } def "PBS shouldn't exclude bidder when bundle not match with condition"() { @@ -3594,15 +3802,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -3612,13 +3823,16 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) } //todo: BundleIn def "PBS should reject processing the rule engine when the bundleIn schema function contains incompatible arguments"() { - given: "Default bid request with multiply bidders" + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) app.bundle = PBSUtils.randomString @@ -3645,15 +3859,8 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Bid response should contain all requested bidders" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - and: "Analytics result should contain info about rule engine" - def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code - assert it.status == ERROR - def impResult = it.results[0] - assert impResult.status == ERROR - assert impResult.values.message == "Function ${BUNDLE_IN} configuration is invalid: Field 'bundles' is required and has to be an array of string" - } + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid @@ -3661,6 +3868,11 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + + then: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Failed to parse rule-engine config for account ${bidRequest.accountId}: " + + "Function '${BUNDLE_IN}' configuration is invalid: Field 'bundles' is required and has to be an array of strings").size() == 1 } def "PBS should exclude bidder when bundleIn match with condition"() { @@ -3673,42 +3885,40 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Create account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0] = new RuleEngineModelSchema(function: BUNDLE_IN) + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = BUNDLE_IN + it.args = new RuleEngineFunctionArgs(bundles: [bundle]) + } } and: "Save account with rule engine config" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Account cache" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == - REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version @@ -3718,6 +3928,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE } def "PBS shouldn't exclude bidder when bundleIn not match with condition"() { @@ -3735,19 +3952,22 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - and: "Save account with disabled or without rules engine" + and: "Save account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Account cache" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -3757,13 +3977,16 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) } - //todo: mediaTypeIn + //todo: mediaTypeIn UPDATE_LOGIC WITH MULTI TYPES def "PBS should reject processing the rule engine when the mediaTypeIn schema function contains incompatible arguments"() { - given: "Default bid request with multiply bidders" + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } @@ -3789,16 +4012,6 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Bid response should contain all requested bidders" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - and: "Analytics result should contain info about rule engine" - def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code - assert it.status == ERROR - def impResult = it.results[0] - assert impResult.status == ERROR - assert impResult.values.message == "Function ${MEDIA_TYPE_IN} configuration is invalid: Field 'types' is required and has to be an array of string" - } - and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid @@ -3806,6 +4019,14 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + then: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Failed to parse rule-engine config for account ${bidRequest.accountId}: " + + "Function '${MEDIA_TYPE_IN}' configuration is invalid: Field 'types' is required and has to be an array of strings").size() == 1 + where: mediaTypeInArgs << [null, PBSUtils.randomNumber] } @@ -3814,17 +4035,17 @@ class RuleEngineSpec extends ModuleBaseSpec { given: "Default bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - it.imp.add(getDefaultImpression(VIDEO)) - it.imp[1].ext.prebid.bidder.amx = new Amx() - it.imp[1].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[1].ext.prebid.bidder.generic = new Generic() } + and: "Setup bidder response" + def bidderResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidderResponse) + and: "Create account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { it.function = MEDIA_TYPE_IN - it.args = new RuleEngineFunctionArgs(types: [VIDEO.value]) + it.args = new RuleEngineFunctionArgs(types: [BANNER.value]) } } @@ -3832,34 +4053,30 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version @@ -3869,6 +4086,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE } def "PBS shouldn't exclude bidder when mediaTypeIn not match with condition"() { @@ -3889,15 +4113,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -3907,7 +4134,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) } //todo: adUnitCode @@ -3925,38 +4152,38 @@ class RuleEngineSpec extends ModuleBaseSpec { } } + and: "Save storedImp into DB" + def storedImp = StoredImp.getStoredImp(bidRequest) + storedImpDao.save(storedImp) + and: "Save account with rule engine config" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version @@ -3967,34 +4194,37 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + where: bidRequestClosure << [ { tagId -> getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - imp.add(defaultImpression) - imp[1].tagId = tagId + imp[0].tagId = tagId } }, { gpid -> getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - imp.add(defaultImpression) - imp[1].ext = new ImpExt(gpid: gpid) + imp[0].ext.gpid = gpid } }, { pbAdSlot -> getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - imp.add(defaultImpression) - imp[1].ext = new ImpExt(data: new ImpExtContextData(pbAdSlot: pbAdSlot)) + imp[0].ext.data = new ImpExtContextData(pbAdSlot: pbAdSlot) } }, { storedRequestId -> getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - imp.add(defaultImpression) - imp[1].ext = new ImpExt(prebid: new ImpExtPrebid(storedRequest: new PrebidStoredRequest(id: storedRequestId))) + imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: storedRequestId) } } ] @@ -4008,22 +4238,28 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Create account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0] = new RuleEngineModelSchema(function: AD_UNIT_CODE) + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: AD_UNIT_CODE)] + rules[0].conditions = [PBSUtils.randomString] + } } and: "Save account with disabled or without rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -4033,19 +4269,22 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) } - //todo: adUnitCodeIn + //todo: adUnitCodeIn Need update from Alex !!!!!!!!!!!!!!! def "PBS should reject processing the rule engine when the adUnitCodeIn schema function contains incompatible arguments"() { - given: "Default bid request with multiply bidders" + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) imp[0].tagId = PBSUtils.randomString - imp[0].ext = new ImpExt(gpid: PBSUtils.randomString) - imp[0].ext = new ImpExt(data: new ImpExtContextData(pbAdSlot: PBSUtils.randomString)) - imp[0].ext = new ImpExt(prebid: new ImpExtPrebid(storedRequest: new PrebidStoredRequest(id: PBSUtils.randomString))) + imp[0].ext.gpid = PBSUtils.randomString + imp[0].ext.data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) + imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomString) } and: "Save storedImp into DB" @@ -4075,16 +4314,6 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Bid response should contain all requested bidders" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - and: "Analytics result should contain info about rule engine" - def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code - assert it.status == ERROR - def impResult = it.results[0] - assert impResult.status == ERROR - assert impResult.values.message == "Function ${AD_UNIT_CODE} configuration is invalid: Field 'type' is required and has to be an array of string" - } - and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid @@ -4092,6 +4321,14 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Failed to parse rule-engine config for account ${bidRequest.accountId}: " + + "Function '${AD_UNIT_CODE}' configuration is invalid: No arguments allowed").size() == 1 + where: arguments << [PBSUtils.randomBoolean, PBSUtils.randomNumber] } @@ -4109,19 +4346,26 @@ class RuleEngineSpec extends ModuleBaseSpec { } } + and: "Save storedImp into DB" + def storedImp = StoredImp.getStoredImp(bidRequest) + storedImpDao.save(storedImp) + and: "Save account with rule engine config" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -4137,7 +4381,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[1].modelGroups[0] @@ -4157,36 +4401,36 @@ class RuleEngineSpec extends ModuleBaseSpec { getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) imp[0].tagId = PBSUtils.randomString - imp[0].ext = new ImpExt(gpid: PBSUtils.randomString) - imp[0].ext = new ImpExt(data: new ImpExtContextData(pbAdSlot: PBSUtils.randomString)) - imp[0].ext = new ImpExt(prebid: new ImpExtPrebid(storedRequest: new PrebidStoredRequest(id: storedRequestId))) + imp[0].ext.gpid = PBSUtils.randomString + imp[0].ext.data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) + imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: storedRequestId) } }, { pbAdSlot -> getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) imp[0].tagId = PBSUtils.randomString - imp[0].ext = new ImpExt(gpid: PBSUtils.randomString) - imp[0].ext = new ImpExt(data: new ImpExtContextData(pbAdSlot: pbAdSlot)) - imp[0].ext = new ImpExt(prebid: new ImpExtPrebid(storedRequest: new PrebidStoredRequest(id: PBSUtils.randomString))) + imp[0].ext.gpid = PBSUtils.randomString + imp[0].ext.data = new ImpExtContextData(pbAdSlot: pbAdSlot) + imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomString) } }, { gpid -> getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) imp[0].tagId = PBSUtils.randomString - imp[0].ext = new ImpExt(gpid: gpid) - imp[0].ext = new ImpExt(data: new ImpExtContextData(pbAdSlot: PBSUtils.randomString)) - imp[0].ext = new ImpExt(prebid: new ImpExtPrebid(storedRequest: new PrebidStoredRequest(id: PBSUtils.randomString))) + imp[0].ext.gpid = gpid + imp[0].ext.data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) + imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomString) } }, { tagId -> getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) imp[0].tagId = tagId - imp[0].ext = new ImpExt(gpid: PBSUtils.randomString) - imp[0].ext = new ImpExt(data: new ImpExtContextData(pbAdSlot: PBSUtils.randomString)) - imp[0].ext = new ImpExt(prebid: new ImpExtPrebid(storedRequest: new PrebidStoredRequest(id: PBSUtils.randomString))) + imp[0].ext.gpid = PBSUtils.randomString + imp[0].ext.data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) + imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomString) } } ] @@ -4215,10 +4459,10 @@ class RuleEngineSpec extends ModuleBaseSpec { then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -4228,7 +4472,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) } //todo: deviceType @@ -4253,34 +4497,30 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version @@ -4290,6 +4530,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE } def "PBS shouldn't exclude bidder when deviceType not match with condition"() { @@ -4311,15 +4558,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -4329,19 +4579,21 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) } //todo: deviceTypeIn def "PBS should reject processing the rule engine when the deviceTypeIn schema function contains incompatible arguments"() { - given: "Default bid request with multiply bidders" + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) device = new Device(devicetype: PBSUtils.getRandomEnum(DeviceType)) } - and: "Create account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { @@ -4363,22 +4615,21 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Bid response should contain all requested bidders" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - and: "Analytics result should contain info about rule engine" - def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code - assert it.status == ERROR - def impResult = it.results[0] - assert impResult.status == ERROR - assert impResult.values.message == "Function ${DEVICE_COUNTRY_IN} configuration is invalid: Field 'types' is required and has to be an array of string" - } - and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Failed to parse rule-engine config for account ${bidRequest.accountId}: " + + "Function '${DEVICE_COUNTRY_IN}' configuration is invalid: " + + "Field 'countries' is required and has to be an array of strings").size() == 1 } def "PBS should exclude bidder when deviceTypeIn match with condition"() { @@ -4393,7 +4644,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { it.function = DEVICE_TYPE_IN - it.args = new RuleEngineFunctionArgs(codes: [deviceType.value]) + it.args = new RuleEngineFunctionArgs(types: [deviceType.value]) } } @@ -4401,34 +4652,30 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, AMX] + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == RULE_ENGINE.code + assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version @@ -4438,6 +4685,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid assert impResult.appliedTo.impIds == bidRequest.imp.id + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE } def "PBS shouldn't exclude bidder when deviceTypeIn not match with condition"() { @@ -4459,15 +4713,18 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC, OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id).size() == BIDDERS_REQUESTED + assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -4477,7 +4734,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid and: "Analytics result shouldn't contain info about module exclude" - assert getAnalyticResults(bidResponse).isEmpty() + assert !getAnalyticResults(bidResponse) } From 12a450e0a929e2560f43976e70b134d14065cd5d Mon Sep 17 00:00:00 2001 From: markiian Date: Wed, 30 Jul 2025 11:50:00 +0300 Subject: [PATCH 05/18] Major update of rule engine functional tests --- .../server/functional/model/ModuleName.groovy | 2 +- .../config/ModuleHookImplementation.groovy | 2 +- .../model/config/PbsModulesConfig.groovy | 2 +- .../model/config/ResultFunction.groovy | 2 +- .../model/config/RuleEngineFunction.groovy | 2 +- .../config/RuleEngineFunctionArgs.groovy | 4 +- .../RuleEngineModelRuleResultsArgs.groovy | 4 +- .../auction/BidRejectionReason.groovy | 2 +- .../model/response/auction/ModuleValue.groovy | 33 +- .../tests/module/ModuleBaseSpec.groovy | 18 +- .../module/pbruleengine/RuleEngineSpec.groovy | 1349 +++++++++-------- 11 files changed, 783 insertions(+), 637 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/model/ModuleName.groovy b/src/test/groovy/org/prebid/server/functional/model/ModuleName.groovy index ee036e4c703..9aee1c69c68 100644 --- a/src/test/groovy/org/prebid/server/functional/model/ModuleName.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/ModuleName.groovy @@ -8,7 +8,7 @@ enum ModuleName { PB_RESPONSE_CORRECTION ("pb-response-correction"), ORTB2_BLOCKING("ortb2-blocking"), PB_REQUEST_CORRECTION('pb-request-correction'), - RULE_ENGINE('rule-engine') + PB_RULE_ENGINE('pb-rule-engine') @JsonValue final String code diff --git a/src/test/groovy/org/prebid/server/functional/model/config/ModuleHookImplementation.groovy b/src/test/groovy/org/prebid/server/functional/model/config/ModuleHookImplementation.groovy index cb74da0fb17..4d9424e1c39 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/ModuleHookImplementation.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/ModuleHookImplementation.groovy @@ -11,7 +11,7 @@ enum ModuleHookImplementation { ORTB2_BLOCKING_BIDDER_REQUEST("ortb2-blocking-bidder-request"), ORTB2_BLOCKING_RAW_BIDDER_RESPONSE("ortb2-blocking-raw-bidder-response"), PB_REQUEST_CORRECTION_PROCESSED_AUCTION_REQUEST("pb-request-correction-processed-auction-request"), - PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST("rule-engine-processed-auction-request") + PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST("pb-rule-engine-processed-auction-request") @JsonValue final String code diff --git a/src/test/groovy/org/prebid/server/functional/model/config/PbsModulesConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/PbsModulesConfig.groovy index dbdddbf1473..801178fd4d4 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/PbsModulesConfig.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/PbsModulesConfig.groovy @@ -13,5 +13,5 @@ class PbsModulesConfig { Ortb2BlockingConfig ortb2Blocking PbResponseCorrection pbResponseCorrection PbRequestCorrectionConfig pbRequestCorrection - PbRulesEngine ruleEngine + PbRulesEngine pbRuleEngine } diff --git a/src/test/groovy/org/prebid/server/functional/model/config/ResultFunction.groovy b/src/test/groovy/org/prebid/server/functional/model/config/ResultFunction.groovy index 11383006d2d..108e8a00fe0 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/ResultFunction.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/ResultFunction.groovy @@ -8,7 +8,7 @@ enum ResultFunction { EXCLUDE_BIDDER("excludeBidders"), LOG_A_TAG("logAtag") - private String value + String value ResultFunction(String value) { this.value = value diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy index dea4dcb251d..7f747f73e1c 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy @@ -7,7 +7,7 @@ enum RuleEngineFunction { DEVICE_COUNTRY("deviceCountry"), DEVICE_COUNTRY_IN("deviceCountryIn"), DATA_CENTER("dataCenter"), - DATA_CENTER_IN("datacenterIn"), + DATA_CENTER_IN("dataCenterIn"), CHANNEL("channel"), EID_AVAILABLE("eidAvailable"), EID_IN("eidIn"), diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy index 55110b050b0..fbb61c6961b 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy @@ -9,7 +9,7 @@ class RuleEngineFunctionArgs { List sources List sids Object pct - String key + Object key List domains List bundles List codes @@ -24,7 +24,7 @@ class RuleEngineFunctionArgs { datacenters = [PBSUtils.randomString] sources = [PBSUtils.randomString] sids = [PBSUtils.randomNumber] - pct = PBSUtils.randomString + pct = PBSUtils.getRandomNumber(1, 100) key = PBSUtils.randomString domains = [PBSUtils.randomString] bundles = [PBSUtils.randomString] diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy index 4d5c177b427..c5c48a0a630 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy @@ -1,12 +1,13 @@ package org.prebid.server.functional.model.config import org.prebid.server.functional.model.bidder.BidderName +import org.prebid.server.functional.model.response.auction.BidRejectionReason import org.prebid.server.functional.util.PBSUtils class RuleEngineModelRuleResultsArgs { List bidders - Integer seatNonBid + BidRejectionReason seatNonBid String analyticsValue Boolean ifSyncedId @@ -14,7 +15,6 @@ class RuleEngineModelRuleResultsArgs { new RuleEngineModelRuleResultsArgs().tap { it.bidders = [bidderName] it.analyticsValue = PBSUtils.randomString - it.seatNonBid = 201 } } diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/BidRejectionReason.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidRejectionReason.groovy index 2c8007113bf..98b8dafb43e 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/BidRejectionReason.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidRejectionReason.groovy @@ -14,7 +14,7 @@ enum BidRejectionReason { REQUEST_BLOCKED_GENERAL(200), REQUEST_BLOCKED_UNSUPPORTED_CHANNEL(201), REQUEST_BLOCKED_UNSUPPORTED_MEDIA_TYPE(202), - REQUEST_BIDDER_REMOVED_BY_MODULE(203), + REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE(203), REQUEST_BLOCKED_PRIVACY(204), REQUEST_BLOCKED_UNACCEPTABLE_CURRENCY(205), diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy index 986bf8dafd9..d17db07a968 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/ModuleValue.groovy @@ -1,35 +1,22 @@ package org.prebid.server.functional.model.response.auction -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.PropertyNamingStrategies -import com.fasterxml.jackson.databind.annotation.JsonNaming import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import org.prebid.server.functional.model.ModuleName -import org.prebid.server.functional.model.config.ResultFunction import org.prebid.server.functional.model.bidder.BidderName @ToString(includeNames = true, ignoreNulls = true) @EqualsAndHashCode class ModuleValue { - ModuleName module - String richmediaFormat - @JsonProperty("analytics_key") - String analyticsKey - @JsonProperty("analytics_value") - String analyticsValue - @JsonProperty("model_version") - String modelVersion - @JsonProperty("condition_fired") - List conditionFired - @JsonProperty("rule_fired") - String rule_fired - @JsonProperty("result_function") - String resultFunctions - @JsonProperty("bidders_removed") - List biddersRemoved - @JsonProperty("seat_non_bid") - String seatNonBid - String message + ModuleName module + String richmediaFormat + String analyticsKey + String analyticsValue + String modelVersion + String conditionFired + String resultFunction + List biddersRemoved + BidRejectionReason seatNonBid + String message } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy index 4b010516d5d..c5eb80cc8e4 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy @@ -11,7 +11,7 @@ import org.prebid.server.functional.tests.BaseSpec import static org.prebid.server.functional.model.ModuleName.ORTB2_BLOCKING import static org.prebid.server.functional.model.ModuleName.PB_RESPONSE_CORRECTION import static org.prebid.server.functional.model.ModuleName.PB_RICHMEDIA_FILTER -import static org.prebid.server.functional.model.ModuleName.RULE_ENGINE +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE import static org.prebid.server.functional.model.config.Endpoint.OPENRTB2_AUCTION import static org.prebid.server.functional.model.config.Stage.ALL_PROCESSED_BID_RESPONSES import static org.prebid.server.functional.model.config.Stage.PROCESSED_AUCTION_REQUEST @@ -60,14 +60,14 @@ class ModuleBaseSpec extends BaseSpec { } protected static Map getRulesEngineSettings(Endpoint endpoint = OPENRTB2_AUCTION, Stage stage = PROCESSED_AUCTION_REQUEST) { - ["hooks.${RULE_ENGINE.code}.enabled" : "true", - "hooks.${RULE_ENGINE.code}.rule-cache.expire-after-minutes": "123123123123", - "hooks.${RULE_ENGINE.code}.rule-cache.max-size" : "200", - "hooks.rule-engine.rule-parsing.retry-initial-delay-millis": "1", - "hooks.rule-engine.rule-parsing.retry-max-delay-millis" : "1", - "hooks.rule-engine.rule-parsing.retry-exponential-factor" : "1.2", - "hooks.rule-engine.rule-parsing.retry-exponential-jitter" : "1.2", - "hooks.host-execution-plan" : encode(ExecutionPlan.getSingleEndpointExecutionPlan(endpoint, RULE_ENGINE, [stage]))] + ["hooks.${PB_RULE_ENGINE.code}.enabled" : "true", + "hooks.${PB_RULE_ENGINE.code}.rule-cache.expire-after-minutes": "123123123123", + "hooks.${PB_RULE_ENGINE.code}.rule-cache.max-size" : "200", + "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-initial-delay-millis" : "1", + "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-max-delay-millis" : "1", + "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-exponential-factor" : "1.2", + "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-exponential-jitter" : "1.2", + "hooks.host-execution-plan" : encode(ExecutionPlan.getSingleEndpointExecutionPlan(endpoint, PB_RULE_ENGINE, [stage]))] } protected static List getAnalyticResults(BidResponse response) { diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy index bab7d76b96b..c1a23406de6 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy @@ -32,9 +32,7 @@ import org.prebid.server.functional.model.request.auction.DistributionChannel 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.Imp -import org.prebid.server.functional.model.request.auction.ImpExt import org.prebid.server.functional.model.request.auction.ImpExtContextData -import org.prebid.server.functional.model.request.auction.ImpExtPrebid import org.prebid.server.functional.model.request.auction.Kvps import org.prebid.server.functional.model.request.auction.PrebidStoredRequest import org.prebid.server.functional.model.request.auction.Publisher @@ -46,21 +44,18 @@ import org.prebid.server.functional.model.request.auction.UserExt import org.prebid.server.functional.model.request.auction.UserExtData import org.prebid.server.functional.model.response.auction.BidRejectionReason import org.prebid.server.functional.model.response.auction.BidResponse -import org.prebid.server.functional.model.response.auction.ResponseAction import org.prebid.server.functional.service.PrebidServerService import org.prebid.server.functional.tests.module.ModuleBaseSpec import org.prebid.server.functional.util.HttpUtil import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.TcfConsent import spock.lang.IgnoreRest -import spock.lang.RepeatUntilFailure import java.time.Instant import static java.lang.Boolean.FALSE import static java.lang.Boolean.TRUE import static org.prebid.server.functional.model.ChannelType.WEB -import static org.prebid.server.functional.model.ModuleName.RULE_ENGINE import static org.prebid.server.functional.model.bidder.BidderName.ALIAS import static org.prebid.server.functional.model.bidder.BidderName.AMX import static org.prebid.server.functional.model.bidder.BidderName.GENERIC @@ -74,6 +69,7 @@ import static org.prebid.server.functional.model.config.RuleEngineFunction.AD_UN import static org.prebid.server.functional.model.config.RuleEngineFunction.BUNDLE import static org.prebid.server.functional.model.config.RuleEngineFunction.BUNDLE_IN import static org.prebid.server.functional.model.config.RuleEngineFunction.CHANNEL +import static org.prebid.server.functional.model.config.RuleEngineFunction.DATA_CENTER import static org.prebid.server.functional.model.config.RuleEngineFunction.DATA_CENTER_IN import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY_IN @@ -100,13 +96,11 @@ import static org.prebid.server.functional.model.pricefloors.MediaType.BANNER import static org.prebid.server.functional.model.request.auction.DistributionChannel.APP import static org.prebid.server.functional.model.request.auction.DistributionChannel.DOOH import static org.prebid.server.functional.model.request.auction.DistributionChannel.SITE -import static org.prebid.server.functional.model.request.auction.FetchStatus.ERROR import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS import static org.prebid.server.functional.model.request.auction.Imp.getDefaultImpression import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE import static org.prebid.server.functional.model.response.auction.BidRejectionReason.ERROR_NO_BID -import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_MODULE -import static org.prebid.server.functional.model.response.auction.MediaType.* +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE import static org.prebid.server.functional.testcontainers.Dependencies.getNetworkServiceContainer import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID import static org.prebid.server.functional.util.privacy.TcfConsent.PurposeId.BASIC_ADS @@ -115,16 +109,16 @@ class RuleEngineSpec extends ModuleBaseSpec { private static final Integer BIDDERS_REQUESTED = 3 private static final Integer ONE_BIDDER_REQUESTED = 1 - private static final String WILDCARD = "*" + private static final String APPLIED_FOR_ALL_IMPS = "*" private static final Map OPENX_CONFIG = ["adapters.${OPENX}.enabled" : "true", "adapters.${OPENX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] private static final Map AMX_CONFIG = ["adapters.${AMX}.enabled" : "true", "adapters.${AMX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] private static final Map OPENX_ALIAS_CONFIG = ["adapters.${OPENX}.aliases.${OPENX_ALIAS}.enabled" : "true", "adapters.${OPENX}.aliases.${OPENX_ALIAS}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] - private static final String DATA_CENTER = PBSUtils.randomString + private static final String CONFIG_DATA_CENTER = PBSUtils.randomString private static final PrebidServerService pbsServiceWithRulesEngineModule = pbsServiceFactory.getService( - getRulesEngineSettings() + AMX_CONFIG + OPENX_CONFIG + OPENX_ALIAS_CONFIG + ['datacenter-region': DATA_CENTER]) + getRulesEngineSettings() + AMX_CONFIG + OPENX_CONFIG + OPENX_ALIAS_CONFIG + ['datacenter-region': CONFIG_DATA_CENTER]) //todo test cases: DONE // If 'exclude', then remove these bidders from imp[n].ext.prebid.bidders @@ -268,6 +262,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } //todo what should happened when updateBidRequestWithTraceVerboseAndReturnAllBidStatus absent and validation failed + def "PBS should logAtag when default model doesn't exist"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -304,18 +299,20 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code - assert it.status == ERROR - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - def impResult = it.results[0] - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == [LOG_A_TAG.toString()] - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.appliedTo.impIds == [WILDCARD] - } + def result = analyticsResult[0] + assert result.name == "rules-filter" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] } def "PBS shouldn't remove bidder when rule engine disabled or absent in account"() { @@ -427,7 +424,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS should remove bidder from imps and use default 203 value for seatNonBid when seatNonBid null and exclude bidder in account config"() { @@ -466,30 +463,29 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == "rules-filter" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + and: "Response should populate seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code - assert it.status == SUCCESS - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - def impResult = it.results[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id - } + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS should remove bidder from imps and update seatNonBid with other code when seatNonBid override and exclude bidder in account config"() { @@ -508,7 +504,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def bidRejectionReason = PBSUtils.getRandomEnum(BidRejectionReason) def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args.seatNonBid = bidRejectionReason.code + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.seatNonBid = bidRejectionReason } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -529,30 +525,29 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == "rules-filter" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + and: "Response should populate seatNon bid" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == bidRejectionReason - - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == RULE_ENGINE.code - assert it.status == SUCCESS - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - def impResult = it.results[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id - } } def "PBS should remove bidder from imps and not update seatNonBid when returnAllBidStatus disabled and exclude bidder in account config"() { @@ -573,7 +568,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args.seatNonBid = ERROR_NO_BID.code + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.seatNonBid = ERROR_NO_BID } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -599,25 +594,23 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-filter" - assert it.status == SUCCESS - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - def impResult = it.results[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id - } + def result = analyticsResult[0] + assert result.name == "rules-filter" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] } - //todo UNKNONW BIDDER ADDED - ANSWERED PLEASE PAY ATTENTION - def "PBS shouldn't remove bidder and don't update analytics when bidder doesn't specified with account and request"() { + def "PBS TODO when bidder doesn't specified with account and request"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) @@ -650,9 +643,6 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about rule engine" //todo WHY HERE Updated - assert getInvocationResult(bidResponse)[0].action == ResponseAction.NO_INVOCATION } def "PBS should include one bidder and update analytics when multiple bidders specified and one included in account"() { @@ -690,21 +680,27 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-filter" - assert it.status == SUCCESS - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - def impResult = it.results[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == [GENERIC, AMX] - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == ["*"] - } + def result = analyticsResult[0] + assert result.name == "rules-filter" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == [GENERIC, AMX].sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + + and: "Response should populate seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS should remove bidder by device geo from imps when bidder excluded in account config"() { @@ -741,30 +737,29 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == "rules-filter" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + and: "Response should populate seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-filter" - assert it.status == SUCCESS - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - def impResult = it.results[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == [GENERIC, GENERIC] - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id - } + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS should leave only include bidder at imps when bidder include in account config"() { @@ -803,26 +798,29 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-filter" - assert it.status == SUCCESS - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - def impResult = it.results[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == [OPENX, AMX] - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id - } + def result = analyticsResult[0] + assert result.name == "rules-filter" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved == [AMX, OPENX] + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + + and: "Response should populate seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS should only logATag when function log a tag only"() { @@ -865,19 +863,20 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-analytics-logger" - assert it.status == SUCCESS - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - def impResult = it.results[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.modelVersion == groups.version - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.appliedTo.impIds == bidRequest.imp.id - } + def result = analyticsResult[0] + assert result.name == "rules-analytics-logger" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert !impResult.values.biddersRemoved + assert !impResult.values.seatNonBid + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] } //todo: Hard alias @@ -921,29 +920,29 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - def seatNonBid = bidResponse.ext.seatnonbid - assert seatNonBid.seat == [OPENX, AMX, GENERIC] - assert seatNonBid.nonBid[0].impId == [bidRequest.imp[0].id] - assert seatNonBid.nonBid[0].statusCode == [REQUEST_BIDDER_REMOVED_BY_MODULE] - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-filter" - assert it.status == SUCCESS - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - def impResult = it.results[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == [GENERIC, OPENX, AMX, GENERIC, OPENX, AMX] - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id - } + def result = analyticsResult[0] + assert result.name == "rules-filter" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == [GENERIC, OPENX, AMX] + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + + and: "Response should populate seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == [OPENX, AMX, GENERIC].sort() + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS should remove hard alias bidder from imps when hard alias bidder excluded in account config"() { @@ -983,30 +982,29 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == "rules-filter" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX_ALIAS assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-filter" - assert it.status == SUCCESS - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - def impResult = it.results[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == [OPENX_ALIAS] - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id - } + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } //todo: Soft alias @@ -1050,29 +1048,28 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == "rules-filter" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == [OPENX, GENERIC, AMX].sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + and: "Response should seatNon bid with code 203" def seatNonBid = bidResponse.ext.seatnonbid assert seatNonBid.seat == [GENERIC, OPENX, AMX] assert seatNonBid.nonBid[0].impId == [bidRequest.imp[0].id] - assert seatNonBid.nonBid[0].statusCode == [REQUEST_BIDDER_REMOVED_BY_MODULE] - - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-filter" - assert it.status == SUCCESS - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - def impResult = it.results[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == [GENERIC, OPENX, AMX, OPENX, AMX] - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id - } + assert seatNonBid.nonBid[0].statusCode == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] } def "PBS should remove soft alias bidder from imps when soft alias bidder excluded in account config"() { @@ -1114,30 +1111,29 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == "rules-filter" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved == [ALIAS] + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == ALIAS assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-filter" - assert it.status == SUCCESS - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - def impResult = it.results[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == [ALIAS] - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id - } + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS should remove bidder and update analytics when first rule sets disabled and second enabled in account config"() { @@ -1174,30 +1170,29 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == "rules-filter" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-filter" - assert it.status == SUCCESS - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - def impResult = it.results[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id - } + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS should skip rule set and take next one when rule sets not a processed auction request"() { @@ -1208,7 +1203,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } and: "Account with rules engine and several rule sets" - def pbRulesEngine = createRulesEngineWithRule().tap { + def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC), createRuleEngineModelRuleWithExcludeResult(AMX), createRuleEngineModelRuleWithExcludeResult(OPENX)] @@ -1216,7 +1211,7 @@ class RuleEngineSpec extends ModuleBaseSpec { it.ruleSets.add(RuleSets.createRuleSets()) it.ruleSets[1].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(AMX)] } - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRulesEngine) + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) and: "Cache account" @@ -1238,29 +1233,29 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRulesEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE where: stage << Stage.values() - Stage.PROCESSED_AUCTION_REQUEST @@ -1338,7 +1333,7 @@ class RuleEngineSpec extends ModuleBaseSpec { then: "Bid response should contain seats" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 - assert bidResponse.seatbid.seat == [OPENX, AMX] + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -1347,29 +1342,29 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS shouldn't remove bidder from imps when bidder hasn't ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { @@ -1492,29 +1487,30 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - def seatNonBid = bidResponse.ext.seatnonbid - assert seatNonBid.seat == [OPENX, AMX] - assert seatNonBid.nonBid[0].impId == [bidRequest.imp[0].id] - assert seatNonBid.nonBid[0].statusCode == [REQUEST_BIDDER_REMOVED_BY_MODULE] - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-filter" - assert it.status == SUCCESS - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - def impResult = it.results[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == [OPENX, AMX] - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id - } + def result = analyticsResult[0] + assert result.name == "rules-filter" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == [OPENX, AMX] + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == [OPENX, GENERIC] + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } def "PBS should leave request bidder at imps when bidder hasn't ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { @@ -1637,34 +1633,34 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[1] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } - //todo Default leaf need confirm + //todo Default leaf | NEED READ A DOCK - def "PBS should log the default model group and shouldn't modify response when other rules don't fire"() { + def "PBS should log the default model group and shouldn't modify response when other rules not fire"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { it.device = new Device(geo: new Geo(country: BULGARIA)) @@ -1702,24 +1698,29 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-filter" - assert it.status == SUCCESS - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - def impResult = it.results[0] - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == [LOG_A_TAG.toString()] - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.appliedTo.impIds == [WILDCARD] - } + def result = analyticsResult[0] + assert result.name == "rules-filter" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] } //todo: Validation with schema function , SAME CURRENTLY IT's In log def "PBS should reject processing rule engine when #function schema function contain args"() { - given: "Default bid request with multiply bidders" + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } @@ -1736,7 +1737,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -1746,41 +1749,195 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Bid response should contain all requested bidders" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - and: "Analytics result should contain info about rule engine" + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Failed to parse rule-engine config for account ${bidRequest.accountId}: " + + "Function '${function.value}' configuration is invalid: No arguments allowed") + + where: + function << [DEVICE_TYPE, AD_UNIT_CODE, BUNDLE, DOMAIN, TCF_IN_SCOPE, GPP_SID_AVAILABLE, FPD_AVAILABLE, + USER_FPD_AVAILABLE, EID_AVAILABLE, CHANNEL, DEVICE_COUNTRY] + } + + //todo: device country + + def "PBS should exclude bidder when deviceCountry match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DEVICE_COUNTRY)] + rules[0].conditions = [USA.toString()] + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-filter" - assert it.status == ERROR - def impResult = it.results[0] - assert impResult.status == ERROR + def result = analyticsResult[0] + assert result.name == "rules-filter" + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when deviceCountry not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema[0].function = DEVICE_COUNTRY + rules[0].conditions = [PBSUtils.getRandomEnum(Country, [USA]).toString()] + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Account cache" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + //todo DataCenterIN and dataCenter + + def "PBS should reject processing rule engine when dataCenterIn schema function args contain invalid data"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DATA_CENTER_IN + it.args = new RuleEngineFunctionArgs(countries: [CONFIG_DATA_CENTER]) + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - where: - function << [DEVICE_TYPE, AD_UNIT_CODE, BUNDLE, DOMAIN, TCF_IN_SCOPE, GPP_SID_AVAILABLE, FPD_AVAILABLE, - USER_FPD_AVAILABLE, PERCENT, EID_AVAILABLE, CHANNEL, DATA_CENTER, DEVICE_COUNTRY] + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Function 'dataCenterIn' configuration is invalid: " + + "Field 'datacenters' is required and has to be an array of strings") } - //todo: device country - - def "PBS should exclude bidder when deviceCountry match with condition"() { + def "PBS should exclude bidder when dataCenterIn match with condition"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema = [new RuleEngineModelSchema(function: DEVICE_COUNTRY)] - rules[0].conditions = [USA.toString()] + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DATA_CENTER_IN + it.args = new RuleEngineFunctionArgs(datacenters: [CONFIG_DATA_CENTER]) } } @@ -1811,71 +1968,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE - } - - def "PBS shouldn't exclude bidder when deviceCountry not match with condition"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema[0].function = DEVICE_COUNTRY - rules[0].conditions = [PBSUtils.getRandomEnum(Country, [USA]).toString()] - } - } - - and: "Account with disabled or without rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Account cache" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } - //todo DataCenterIN and dataCenter - - def "PBS should reject processing rule engine when dataCenterIn schema function args contain invalid data"() { + def "PBS shouldn't exclude bidder when dataCentersIn not match with condition"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) @@ -1885,7 +1997,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { it.function = DATA_CENTER_IN - it.args = new RuleEngineFunctionArgs(countries: [DATA_CENTER]) + it.args = new RuleEngineFunctionArgs(datacenters: [PBSUtils.randomString]) } } @@ -1917,7 +2029,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - def "PBS should exclude bidder when dataCentersIn match with condition"() { + def "PBS should exclude bidder when dataCenter match with condition"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) @@ -1925,9 +2037,9 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = DATA_CENTER_IN - it.args = new RuleEngineFunctionArgs(datacenters: [DATA_CENTER]) + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DATA_CENTER)] + rules[0].conditions = [CONFIG_DATA_CENTER] } } @@ -1958,26 +2070,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC + assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } - def "PBS shouldn't exclude bidder when dataCentersIn not match with condition"() { + def "PBS shouldn't exclude bidder when dataCenter not match with condition"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) @@ -1985,9 +2097,9 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = DATA_CENTER_IN - it.args = new RuleEngineFunctionArgs(datacenters: [PBSUtils.randomString]) + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DATA_CENTER)] + rules[0].conditions = [PBSUtils.randomString] } } @@ -2062,23 +2174,23 @@ class RuleEngineSpec extends ModuleBaseSpec { assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS shouldn't exclude bidder when channel not match with condition"() { @@ -2164,23 +2276,23 @@ class RuleEngineSpec extends ModuleBaseSpec { assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS shouldn't exclude bidder when eidAvailable not match with condition"() { @@ -2232,7 +2344,10 @@ class RuleEngineSpec extends ModuleBaseSpec { //todo: EID_IN def "PBS should reject processing rule engine when eidIn schema function args contain invalid data"() { - given: "Bid request with multiply bidders" + given: "Test start time" + def startTime = Instant.now() + + and: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) @@ -2259,22 +2374,20 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Bid response should contain all requested bidders" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - and: "Analytics result should contain info about rule engine" - def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-filter" - assert it.status == ERROR - def impResult = it.results[0] - assert impResult.status == ERROR - assert impResult.values.message == "Function ${EID_IN} configuration is invalid: No arguments allowed" - } - and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Function '${EID_IN}' configuration is invalid: " + + "Field 'sources' is required and has to be an array of strings") } def "PBS should exclude bidder when eidIn match with condition"() { @@ -2320,23 +2433,66 @@ class RuleEngineSpec extends ModuleBaseSpec { assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when eidIn match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + user = new User(eids: [Eid.getDefaultEid()]) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = EID_IN + it.args = new RuleEngineFunctionArgs(sources: [PBSUtils.randomString, PBSUtils.randomString]) + } + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) } //todo: userUfpData @@ -2385,18 +2541,18 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE where: requestedUfpUser << [new User(data: [Data.defaultData], ext: new UserExt(data: UserExtData.FPDUserExtData)), @@ -2405,7 +2561,13 @@ class RuleEngineSpec extends ModuleBaseSpec { } def "PBS shouldn't exclude bidder when userFpdAvailable not match with condition"() { - given: "Account with rule engine config" + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + user = requestedUfpUser + } + + and: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: USER_FPD_AVAILABLE)] } @@ -2438,25 +2600,12 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) where: - bidRequest << [ - getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - user = new User(data: null) - }, - getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - user = new User(data: [null]) - }, - getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - user = new User(ext: new UserExt(data: null)) - }, - getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - user = new User(data: null, ext: new UserExt(data: null)) - } + requestedUfpUser << [new User(data: null), new User(data: [null]), //todo empty + new User(ext: new UserExt(data: null)), + new User(data: null, ext: new UserExt(data: null)) ] } + //todo: fpdAvailable def "PBS should exclude bidder when fpdAvailable match with condition"() { @@ -2497,18 +2646,18 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE where: bidRequest << [ @@ -2584,7 +2733,7 @@ class RuleEngineSpec extends ModuleBaseSpec { }, getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - site.content = new Content(data: [null]) + site.content = new Content(data: [null]) //todo PLEASE TAKE A LOOK site.ext = new SiteExt(data: null) }, getDefaultBidRequestWithMultiplyBidders().tap { @@ -2593,7 +2742,7 @@ class RuleEngineSpec extends ModuleBaseSpec { }, getDefaultBidRequestWithMultiplyBidders(APP).tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - app.content = new Content(data: [null]) + app.content = new Content(data: [null]) //todo PLEASE TAKE A LOOK app.ext = new AppExt(data: null) }, getDefaultBidRequestWithMultiplyBidders(APP).tap { @@ -2649,18 +2798,18 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS shouldn't exclude bidder when gppSidAvailable not match with condition"() { @@ -2804,18 +2953,18 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS shouldn't exclude bidder when gppSidIn not match with condition"() { @@ -2910,23 +3059,23 @@ class RuleEngineSpec extends ModuleBaseSpec { assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE where: gdpr | condition @@ -2989,7 +3138,10 @@ class RuleEngineSpec extends ModuleBaseSpec { //todo: percent def "PBS should reject processing rule engine when percent schema function args contain invalid data"() { - given: "Default bid request with multiply bidders" + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } @@ -3019,14 +3171,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert bidResponse.seatbid.size() == BIDDERS_REQUESTED and: "Analytics result should contain info about rule engine" - def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-filter" - assert it.status == ERROR - def impResult = it.results[0] - assert impResult.status == ERROR - assert impResult.values.message == "Function ${PERCENT} configuration is invalid: Field 'key' is required and has to be an array of string " - } + assert !getAnalyticResults(bidResponse) and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid @@ -3034,6 +3179,11 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Function 'percent' configuration is invalid: " + + "Field 'pct' is required and has to be an integer") } def "PBS should exclude bidder when percent match with condition"() { @@ -3082,18 +3232,18 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE where: percent << [100, PBSUtils.getRandomNumber(100)] @@ -3144,10 +3294,13 @@ class RuleEngineSpec extends ModuleBaseSpec { percent << [0, PBSUtils.randomNegativeNumber] } - //todo: prebidKey !!! CAN BE REMOVED || REINVESTIGATION AFTER UPDATE + //todo: prebidKey def "PBS should reject processing the rule engine when the prebidKey schema function contains incompatible arguments"() { - given: "Default bid request with multiply bidders" + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } @@ -3164,6 +3317,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -3173,22 +3329,20 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Bid response should contain all requested bidders" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - and: "Analytics result should contain info about rule engine" - def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-filter" - assert it.status == ERROR - def impResult = it.results[0] - assert impResult.status == ERROR - assert impResult.values.message == "Function ${PREBID_KEY} configuration is invalid: Field 'key' is required and has to be an array of string" - } - - and: "PBS response shouldn't contain seatNonBid" + and: "PBS response should not contain seatNonBid" assert !bidResponse.ext.seatnonbid and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Function 'prebidKey' configuration is invalid: " + + "Field 'key' is required and has to be a string") } def "PBS should exclude bidder when prebidKey match with condition"() { @@ -3235,23 +3389,23 @@ class RuleEngineSpec extends ModuleBaseSpec { assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS shouldn't exclude bidder when prebidKey not match with condition"() { @@ -3342,18 +3496,18 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE where: distributionChannel | bidRequestClosure @@ -3439,7 +3593,10 @@ class RuleEngineSpec extends ModuleBaseSpec { //todo : domainIN def "PBS should reject processing the rule engine when the domainIn schema function contains incompatible arguments"() { - given: "Default bid request with multiplyB bidders" + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiplyB bidders" def randomDomain = PBSUtils.randomString def bidRequest = bidRequestClosure(randomDomain) @@ -3455,6 +3612,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -3464,16 +3624,6 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Bid response should contain all requested bidders" assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - and: "Analytics result should contain info about rule engine" - def analyticsResult = getAnalyticResults(bidResponse) - verifyAll(analyticsResult.first) { - assert it.name == "rules-filter" - assert it.status == ERROR - def impResult = it.results[0] - assert impResult.status == ERROR - assert impResult.values.message == "Function ${DOMAIN_IN} configuration is invalid: Field 'bundles' is required and has to be an array of string" - } - and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid @@ -3481,6 +3631,14 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Function 'domainIn' configuration is invalid: " + + "Field 'domains' is required and has to be an array of strings") + where: distributionChannel | bidRequestClosure SITE | { String domain -> @@ -3578,18 +3736,18 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE where: distributionChannel | bidRequestClosure @@ -3769,18 +3927,18 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS shouldn't exclude bidder when bundle not match with condition"() { @@ -3923,18 +4081,18 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS shouldn't exclude bidder when bundleIn not match with condition"() { @@ -4081,10 +4239,10 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" @@ -4092,7 +4250,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS shouldn't exclude bidder when mediaTypeIn not match with condition"() { @@ -4188,10 +4346,10 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" @@ -4199,7 +4357,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE where: bidRequestClosure << [ @@ -4272,7 +4430,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - //todo: adUnitCodeIn Need update from Alex !!!!!!!!!!!!!!! + //todo: adUnitCodeIn def "PBS should reject processing the rule engine when the adUnitCodeIn schema function contains incompatible arguments"() { given: "Test start time" @@ -4376,7 +4534,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) @@ -4384,15 +4542,15 @@ class RuleEngineSpec extends ModuleBaseSpec { assert result.name == "rules-filter" assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE assert impResult.appliedTo.impIds == bidRequest.imp.id where: @@ -4525,18 +4683,18 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS shouldn't exclude bidder when deviceType not match with condition"() { @@ -4606,6 +4764,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -4627,9 +4788,8 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Logs should contain error" def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) - assert getLogsByText(logs, "Failed to parse rule-engine config for account ${bidRequest.accountId}: " + - "Function '${DEVICE_COUNTRY_IN}' configuration is invalid: " + - "Field 'countries' is required and has to be an array of strings").size() == 1 + assert getLogsByText(logs, "Function '${DEVICE_COUNTRY_IN}' configuration is invalid: " + + "Field 'countries' is required and has to be an array of strings") } def "PBS should exclude bidder when deviceTypeIn match with condition"() { @@ -4680,18 +4840,18 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.conditionFired == groups.rules.first.conditions - assert impResult.values.resultFunctions == [groups.rules.first.results.first.function] - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == groups.rules.first.results.first.args.seatNonBid - assert impResult.appliedTo.impIds == bidRequest.imp.id + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_MODULE + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS shouldn't exclude bidder when deviceTypeIn not match with condition"() { @@ -4737,7 +4897,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - private static BidRequest getDefaultBidRequestWithMultiplyBidders(DistributionChannel distributionChannel = SITE) { BidRequest.getDefaultBidRequest(distributionChannel).tap { it.imp[0].ext.prebid.bidder.amx = new Amx() @@ -4758,7 +4917,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } private static getAccountWithRulesEngine(String accountId, PbRulesEngine ruleEngine) { - def accountHooksConfiguration = new AccountHooksConfiguration(modules: new PbsModulesConfig(ruleEngine: ruleEngine)) + def accountHooksConfiguration = new AccountHooksConfiguration(modules: new PbsModulesConfig(pbRuleEngine: ruleEngine)) new Account(uuid: accountId, config: new AccountConfig(hooks: accountHooksConfiguration)) } } From 4b7221725db0d92448eacc56768f75741c4b6d85 Mon Sep 17 00:00:00 2001 From: markiian Date: Thu, 21 Aug 2025 23:47:57 +0300 Subject: [PATCH 06/18] Clean up code --- .../model/config/RuleEngineModelSchema.groovy | 68 ------------------- .../config/RulesEngineModelGroups.groovy | 4 +- .../response/auction/ResponseAction.groovy | 2 +- .../tests/module/ModuleBaseSpec.groovy | 16 ++--- 4 files changed, 10 insertions(+), 80 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelSchema.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelSchema.groovy index 49893f5afc9..299ad225a89 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelSchema.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelSchema.groovy @@ -1,19 +1,8 @@ package org.prebid.server.functional.model.config import groovy.transform.ToString -import org.prebid.server.functional.model.pricefloors.MediaType -import org.prebid.server.functional.model.request.GppSectionId -import org.prebid.server.functional.util.PBSUtils -import static org.prebid.server.functional.model.config.RuleEngineFunction.AD_UNIT_CODE_IN -import static org.prebid.server.functional.model.config.RuleEngineFunction.BUNDLE_IN -import static org.prebid.server.functional.model.config.RuleEngineFunction.DATA_CENTER_IN import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY_IN -import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_TYPE_IN -import static org.prebid.server.functional.model.config.RuleEngineFunction.DOMAIN_IN -import static org.prebid.server.functional.model.config.RuleEngineFunction.EID_IN -import static org.prebid.server.functional.model.config.RuleEngineFunction.GPP_SID_IN -import static org.prebid.server.functional.model.config.RuleEngineFunction.MEDIA_TYPE_IN import static org.prebid.server.functional.model.pricefloors.Country.USA @ToString(includeNames = true, ignoreNulls = true) @@ -28,61 +17,4 @@ class RuleEngineModelSchema { it.args = new RuleEngineFunctionArgs(countries: argsCountries) } } - - static RuleEngineModelSchema createDataCenterInSchema(List argsDataCenter = [PBSUtils.randomString]) { - new RuleEngineModelSchema().tap { - it.function = DATA_CENTER_IN - it.args = new RuleEngineFunctionArgs(datacenters: argsDataCenter) - } - } - - static RuleEngineModelSchema createEidInSchema(List argsEid = [PBSUtils.randomString]) { - new RuleEngineModelSchema().tap { - it.function = EID_IN - it.args = new RuleEngineFunctionArgs(sources: argsEid) - } - } - - static RuleEngineModelSchema createGppSidInSchema(List argsGppSid = [PBSUtils.getRandomEnum(GppSectionId)]) { - new RuleEngineModelSchema().tap { - it.function = GPP_SID_IN - it.args = new RuleEngineFunctionArgs(sids: argsGppSid) - } - } - - static RuleEngineModelSchema createDomainInSchema(List argsDomain = [PBSUtils.randomString]) { - new RuleEngineModelSchema().tap { - it.function = DOMAIN_IN - it.args = new RuleEngineFunctionArgs(domains: argsDomain) - } - } - - static RuleEngineModelSchema createBundleInSchema(List argsBundle = [PBSUtils.randomString]) { - new RuleEngineModelSchema().tap { - it.function = BUNDLE_IN - it.args = new RuleEngineFunctionArgs(bundles: argsBundle) - } - } - - static RuleEngineModelSchema createMediaTypeInSchema(List argsMediaType = [PBSUtils.getRandomEnum(MediaType)]) { - new RuleEngineModelSchema().tap { - it.function = MEDIA_TYPE_IN - it.args = new RuleEngineFunctionArgs(types: argsMediaType) - } - } - - static RuleEngineModelSchema createAdUnitInSchema(List argsMediaType = [PBSUtils.randomString]) { - new RuleEngineModelSchema().tap { - it.function = AD_UNIT_CODE_IN - it.args = new RuleEngineFunctionArgs(codes: argsMediaType) - } - } - - static RuleEngineModelSchema createDeviceTypeInSchema(List argsMediaType = [PBSUtils.randomNumber]) { - new RuleEngineModelSchema().tap { - it.function = DEVICE_TYPE_IN - it.args = new RuleEngineFunctionArgs(types: argsMediaType) - } - } - } diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroups.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroups.groovy index 61d1807910d..8d0565aa21e 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroups.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroups.groovy @@ -1,11 +1,9 @@ package org.prebid.server.functional.model.config import com.fasterxml.jackson.annotation.JsonProperty -import org.prebid.server.functional.model.pricefloors.Country import org.prebid.server.functional.util.PBSUtils -import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY_IN -import static org.prebid.server.functional.model.config.RuleEngineModelRule.* +import static org.prebid.server.functional.model.config.RuleEngineModelRule.createRuleEngineModelRule import static org.prebid.server.functional.model.config.RuleEngineModelSchema.createDeviceCountryInSchema class RulesEngineModelGroups { diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/ResponseAction.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/ResponseAction.groovy index 1bce783d048..fbcfa74683d 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/ResponseAction.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/ResponseAction.groovy @@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonValue enum ResponseAction { - UPDATE, NO_ACTION, NO_INVOCATION + UPDATE, NO_ACTION, NO_INVOCATION, REJECT @JsonValue String getValue() { diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy index c5eb80cc8e4..b4cca8b9189 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy @@ -60,14 +60,14 @@ class ModuleBaseSpec extends BaseSpec { } protected static Map getRulesEngineSettings(Endpoint endpoint = OPENRTB2_AUCTION, Stage stage = PROCESSED_AUCTION_REQUEST) { - ["hooks.${PB_RULE_ENGINE.code}.enabled" : "true", - "hooks.${PB_RULE_ENGINE.code}.rule-cache.expire-after-minutes": "123123123123", - "hooks.${PB_RULE_ENGINE.code}.rule-cache.max-size" : "200", - "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-initial-delay-millis" : "1", - "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-max-delay-millis" : "1", - "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-exponential-factor" : "1.2", - "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-exponential-jitter" : "1.2", - "hooks.host-execution-plan" : encode(ExecutionPlan.getSingleEndpointExecutionPlan(endpoint, PB_RULE_ENGINE, [stage]))] + ["hooks.${PB_RULE_ENGINE.code}.enabled" : "true", + "hooks.${PB_RULE_ENGINE.code}.rule-cache.expire-after-minutes" : "10000", + "hooks.${PB_RULE_ENGINE.code}.rule-cache.max-size" : "200", + "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-initial-delay-millis": "1", + "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-max-delay-millis" : "1", + "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-exponential-factor" : "1.2", + "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-exponential-jitter" : "1.2", + "hooks.host-execution-plan" : encode(ExecutionPlan.getSingleEndpointExecutionPlan(endpoint, PB_RULE_ENGINE, [stage]))] } protected static List getAnalyticResults(BidResponse response) { From 71348bc302c9d1576108d572d4fe62ad6a0dadf4 Mon Sep 17 00:00:00 2001 From: markiian Date: Mon, 1 Sep 2025 14:03:36 +0300 Subject: [PATCH 07/18] Update after minor update --- .../module/pbruleengine/RuleEngineSpec.groovy | 371 +++++++++--------- 1 file changed, 176 insertions(+), 195 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy index c1a23406de6..2a2b24b0bfc 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy @@ -49,7 +49,6 @@ import org.prebid.server.functional.tests.module.ModuleBaseSpec import org.prebid.server.functional.util.HttpUtil import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.TcfConsent -import spock.lang.IgnoreRest import java.time.Instant @@ -116,34 +115,11 @@ class RuleEngineSpec extends ModuleBaseSpec { "adapters.${AMX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] private static final Map OPENX_ALIAS_CONFIG = ["adapters.${OPENX}.aliases.${OPENX_ALIAS}.enabled" : "true", "adapters.${OPENX}.aliases.${OPENX_ALIAS}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + private static final String PB_RULE_ENGINE_CODE = "pb-rule-engine" private static final String CONFIG_DATA_CENTER = PBSUtils.randomString private static final PrebidServerService pbsServiceWithRulesEngineModule = pbsServiceFactory.getService( getRulesEngineSettings() + AMX_CONFIG + OPENX_CONFIG + OPENX_ALIAS_CONFIG + ['datacenter-region': CONFIG_DATA_CENTER]) - //todo test cases: DONE - // If 'exclude', then remove these bidders from imp[n].ext.prebid.bidders - // If 'include', then keep only these bidders in imp[n].ext.prebid.bidders - // include case with 2imps and same bidder what it goes - // with alias include / exclude - // few imps - // with ifSyncedId true/false | HALF DONE -> only for exclude - // without analytics key | DONE - // seatNonBid --- default 203 and specified any code - - //todo: PROGRESS - // same weight -> then random I hope we don't tests it since we don't know exactly result and it's potential flaky tests NO-OP - // test case with default - // send seanNonBid when it really needed - - //todo: - // Loop through every imp in the auction - // If adUnitCode was one of the schema functions, and its winning value was other than a wildcard: - // Compare the chosen adUnitCode value against the following ORTB fields: imp[n].ext.gpid, imp[n].tagid, imp[n].ext.data.pbadslot, imp[n].ext.prebid.storedrequest.id - // If none of them match, then skip this imp. - // If mediaType was one of the schema functions, and its winning value was other than a wildcard: - // Compare the chosen mediaType value against the existence of imp.MEDIATYPE, - // If there's no imp.MEDIATYPE, then skip this imp. - def "PBS shouldn't remove bidder when rule engine not fully configured in account"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -261,9 +237,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert getInvocationResult(bidResponse)[0].message == "Rule for account ${bidRequest.accountId} is not ready" } - //todo what should happened when updateBidRequestWithTraceVerboseAndReturnAllBidStatus absent and validation failed - - def "PBS should logAtag when default model doesn't exist"() { + def "PBS shouldn't log default model when rule does not fired and empty model default"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { it.device = new Device(geo: new Geo(country: BULGARIA)) @@ -277,7 +251,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) - and: "Cahce account" + and: "Cache account" pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) when: "PBS processes auction request" @@ -297,22 +271,8 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == "rules-filter" - assert result.status == SUCCESS - def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) } def "PBS shouldn't remove bidder when rule engine disabled or absent in account"() { @@ -466,7 +426,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -478,7 +438,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should populate seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -528,7 +488,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -540,7 +500,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should populate seatNon bid" assert bidResponse.ext.seatnonbid.size() == 1 @@ -595,7 +555,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -607,10 +567,10 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id } - def "PBS TODO when bidder doesn't specified with account and request"() { + def "PBS should when bidder doesn't specified with account and request"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) @@ -619,7 +579,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(UNKNOWN), + it.ruleSets[0].modelGroups[0].rules[0].results = [//createRuleEngineModelRuleWithExcludeResult(UNKNOWN), createRuleEngineModelRuleWithIncludeResult(UNKNOWN)] } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) @@ -681,7 +641,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -693,7 +653,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == [GENERIC, AMX].sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should populate seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -740,7 +700,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -752,7 +712,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should populate seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -801,7 +761,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -813,7 +773,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved == [AMX, OPENX] assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should populate seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -823,7 +783,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } - def "PBS should only logATag when function log a tag only"() { + def "PBS should only logATag when present only function log a tag"() { given: "Bid request with multiply imps bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { it.imp.add(defaultImpression) @@ -864,7 +824,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-analytics-logger" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -879,8 +839,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] } - //todo: Hard alias - def "PBS should leave only hard alias bidder at imps when hard alias bidder include in account config"() { given: "Bid request with multiply imps bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -923,7 +881,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -935,7 +893,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == [GENERIC, OPENX, AMX] assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should populate seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -985,7 +943,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -997,7 +955,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == [bidRequest.imp[1].id] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -1007,8 +965,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } - //todo: Soft alias - def "PBS should leave only soft alias bidder at imps when soft alias bidder include in account config"() { given: "Bid request with multiply imps bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -1051,7 +1007,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -1063,11 +1019,11 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == [OPENX, GENERIC, AMX].sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" def seatNonBid = bidResponse.ext.seatnonbid - assert seatNonBid.seat == [GENERIC, OPENX, AMX] + assert seatNonBid.seat == [ALIAS] assert seatNonBid.nonBid[0].impId == [bidRequest.imp[0].id] assert seatNonBid.nonBid[0].statusCode == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] } @@ -1114,7 +1070,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -1126,7 +1082,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved == [ALIAS] assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == [bidRequest.imp[1].id] and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -1173,10 +1129,10 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] assert impResult.status == SUCCESS assert impResult.values.analyticsKey == groups.analyticsKey assert impResult.values.modelVersion == groups.version @@ -1185,7 +1141,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -1236,7 +1192,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[1].modelGroups[0] @@ -1248,7 +1204,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -1261,8 +1217,6 @@ class RuleEngineSpec extends ModuleBaseSpec { stage << Stage.values() - Stage.PROCESSED_AUCTION_REQUEST } - //todo: if synced with exclude - def "PBS shouldn't remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -1345,7 +1299,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -1357,7 +1311,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -1406,9 +1360,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - //todo: if synced with include - - def "PBS should leave request bidder at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=true in account config"() { + def "PBS should remove requested bidders at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=true in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) @@ -1433,22 +1385,39 @@ class RuleEngineSpec extends ModuleBaseSpec { when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) - then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + then: "Bid response shouldn't contain seat" + assert !bidResponse.seatbid.seat - and: "PBs should perform bidder requests" + and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE_CODE + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == [AMX, OPENX, GENERIC].sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == bidRequest.imp.id - and: "Analytics result shouldn't contain info about rule engine" - assert !getAnalyticResults(bidResponse) + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == [OPENX, GENERIC] + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS should leave only include bidder at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=false in account config"() { @@ -1490,7 +1459,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -1502,7 +1471,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == [OPENX, AMX] assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -1549,8 +1518,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - //todo: add test with weight to be when 0 and 100 - def "PBS shouldn't take rule with higher weight and remove bidder when weight negative or zero"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -1636,7 +1603,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[1] @@ -1648,7 +1615,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -1658,8 +1625,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } - //todo Default leaf | NEED READ A DOCK - def "PBS should log the default model group and shouldn't modify response when other rules not fire"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -1699,7 +1664,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -1711,10 +1676,68 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id } - //todo: Validation with schema function , SAME CURRENTLY IT's In log + def "PBS shouldn't log the default model group and modify response when rules fire"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with default model" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].modelDefault = [new RuleEngineModelDefault( + function: LOG_A_TAG, + args: new RuleEngineModelDefaultArgs(analyticsValue: PBSUtils.randomString))] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain two seat" + assert bidResponse.seatbid.size() == 2 + + and: "Response should contain seat bid" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE_CODE + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == bidRequest.imp.id + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } def "PBS should reject processing rule engine when #function schema function contain args"() { given: "Test start time" @@ -1769,8 +1792,6 @@ class RuleEngineSpec extends ModuleBaseSpec { USER_FPD_AVAILABLE, EID_AVAILABLE, CHANNEL, DEVICE_COUNTRY] } - //todo: device country - def "PBS should exclude bidder when deviceCountry match with condition"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -1810,7 +1831,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -1822,7 +1843,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -1875,8 +1896,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - //todo DataCenterIN and dataCenter - def "PBS should reject processing rule engine when dataCenterIn schema function args contain invalid data"() { given: "Test start time" def startTime = Instant.now() @@ -1965,7 +1984,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -1977,7 +1996,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -2067,7 +2086,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -2079,7 +2098,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -2131,8 +2150,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - //todo: Channel - def "PBS should exclude bidder when channel match with condition"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -2171,7 +2188,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -2183,7 +2200,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -2235,8 +2252,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - //todo: eidAvailable - def "PBS should exclude bidder when eidAvailable match with condition"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -2273,7 +2288,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -2285,7 +2300,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -2341,8 +2356,6 @@ class RuleEngineSpec extends ModuleBaseSpec { eids << [null, []] } - //todo: EID_IN - def "PBS should reject processing rule engine when eidIn schema function args contain invalid data"() { given: "Test start time" def startTime = Instant.now() @@ -2430,7 +2443,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -2442,7 +2455,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -2495,8 +2508,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - //todo: userUfpData - def "PBS should exclude bidder when userFpdAvailable match with condition"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -2533,7 +2544,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -2545,7 +2556,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -2606,8 +2617,6 @@ class RuleEngineSpec extends ModuleBaseSpec { ] } - //todo: fpdAvailable - def "PBS should exclude bidder when fpdAvailable match with condition"() { given: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -2638,7 +2647,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -2650,7 +2659,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -2752,8 +2761,6 @@ class RuleEngineSpec extends ModuleBaseSpec { ] } - //todo: gppSidAvailable - def "PBS should exclude bidder when gppSidAvailable match with condition"() { given: "Default bid request with multiply bidder" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -2790,7 +2797,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -2802,7 +2809,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -2855,8 +2862,6 @@ class RuleEngineSpec extends ModuleBaseSpec { gppSid << [[PBSUtils.randomNegativeNumber], null] } - //todo gppSidIn - def "PBS should reject processing rule engine when gppSidIn schema function args contain invalid data"() { given: "Test start time" def startTime = Instant.now() @@ -2945,7 +2950,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -2957,7 +2962,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -3011,8 +3016,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - //todo: tcfInScope - def "PBS should exclude bidder when tcfInScope match with condition"() { given: "Default bid request with multiply bidder" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -3056,7 +3059,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -3068,7 +3071,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -3135,8 +3138,6 @@ class RuleEngineSpec extends ModuleBaseSpec { 1 | FALSE as String } - //todo: percent - def "PBS should reject processing rule engine when percent schema function args contain invalid data"() { given: "Test start time" def startTime = Instant.now() @@ -3224,7 +3225,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -3236,7 +3237,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -3294,8 +3295,6 @@ class RuleEngineSpec extends ModuleBaseSpec { percent << [0, PBSUtils.randomNegativeNumber] } - //todo: prebidKey - def "PBS should reject processing the rule engine when the prebidKey schema function contains incompatible arguments"() { given: "Test start time" def startTime = Instant.now() @@ -3386,7 +3385,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -3398,7 +3397,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -3449,8 +3448,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - //todo: domain - def "PBS should exclude bidder when domain match with condition"() { given: "Default bid request with multiply bidder" def randomDomain = PBSUtils.randomString @@ -3488,7 +3485,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -3500,7 +3497,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -3590,8 +3587,6 @@ class RuleEngineSpec extends ModuleBaseSpec { } - //todo : domainIN - def "PBS should reject processing the rule engine when the domainIn schema function contains incompatible arguments"() { given: "Test start time" def startTime = Instant.now() @@ -3728,7 +3723,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -3740,7 +3735,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -3877,8 +3872,6 @@ class RuleEngineSpec extends ModuleBaseSpec { } - //todo: Bundle - def "PBS should exclude bidder when bundle match with condition"() { given: "Default bid request with multiply bidder" def bundle = PBSUtils.randomString @@ -3919,7 +3912,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -3931,7 +3924,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -3984,8 +3977,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - //todo: BundleIn - def "PBS should reject processing the rule engine when the bundleIn schema function contains incompatible arguments"() { given: "Test start time" def startTime = Instant.now() @@ -4073,7 +4064,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -4085,7 +4076,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -4138,8 +4129,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - //todo: mediaTypeIn UPDATE_LOGIC WITH MULTI TYPES - def "PBS should reject processing the rule engine when the mediaTypeIn schema function contains incompatible arguments"() { given: "Test start time" def startTime = Instant.now() @@ -4231,7 +4220,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -4295,8 +4284,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - //todo: adUnitCode - def "PBS should exclude bidder when adUnitCode match with condition"() { given: "Default bid request with multiply bidders" def adUnitCode = PBSUtils.randomString @@ -4338,7 +4325,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -4430,8 +4417,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - //todo: adUnitCodeIn - def "PBS should reject processing the rule engine when the adUnitCodeIn schema function contains incompatible arguments"() { given: "Test start time" def startTime = Instant.now() @@ -4529,17 +4514,10 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -4553,6 +4531,13 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE assert impResult.appliedTo.impIds == bidRequest.imp.id + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + where: bidRequestClosure << [ { storedRequestId -> @@ -4633,8 +4618,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - //todo: deviceType - def "PBS should exclude bidder when deviceType match with condition"() { given: "Default bid request with multiply bidders" def deviceType = PBSUtils.getRandomEnum(DeviceType) @@ -4675,7 +4658,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -4687,7 +4670,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -4740,8 +4723,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) } - //todo: deviceTypeIn - def "PBS should reject processing the rule engine when the deviceTypeIn schema function contains incompatible arguments"() { given: "Test start time" def startTime = Instant.now() @@ -4832,7 +4813,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == "rules-filter" + assert result.name == PB_RULE_ENGINE_CODE assert result.status == SUCCESS def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] @@ -4844,7 +4825,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.values.conditionFired == groups.rules.first.conditions.first assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + assert impResult.appliedTo.impIds == bidRequest.imp.id and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 From d35ce57de09d5c5454e6dc7db211fd13fa93f352 Mon Sep 17 00:00:00 2001 From: markiian Date: Mon, 1 Sep 2025 23:36:15 +0300 Subject: [PATCH 08/18] Minor update --- .../module/pbruleengine/RuleEngineSpec.groovy | 104 ++++++++++++++++-- 1 file changed, 96 insertions(+), 8 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy index 2a2b24b0bfc..977266f2961 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy @@ -49,6 +49,7 @@ import org.prebid.server.functional.tests.module.ModuleBaseSpec import org.prebid.server.functional.util.HttpUtil import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.TcfConsent +import spock.lang.IgnoreRest import java.time.Instant @@ -109,6 +110,7 @@ class RuleEngineSpec extends ModuleBaseSpec { private static final Integer BIDDERS_REQUESTED = 3 private static final Integer ONE_BIDDER_REQUESTED = 1 private static final String APPLIED_FOR_ALL_IMPS = "*" + private static final String DEFAULT_CONDITIONS = "default" private static final Map OPENX_CONFIG = ["adapters.${OPENX}.enabled" : "true", "adapters.${OPENX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] private static final Map AMX_CONFIG = ["adapters.${AMX}.enabled" : "true", @@ -343,6 +345,38 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid } + def "PBS shouldn't remove any bidder without cache request"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule() + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + def "PBS should remove bidder and not update analytics when bidder matched with conditions and without analytics key"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -635,9 +669,6 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] @@ -1625,18 +1656,19 @@ class RuleEngineSpec extends ModuleBaseSpec { assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } - def "PBS should log the default model group and shouldn't modify response when other rules not fire"() { + def "PBS shouldn't log the default model group and should modify response when other rule fire"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.device = new Device(geo: new Geo(country: BULGARIA)) + updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with default model" + def analyticsValue = PBSUtils.randomString def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].modelDefault = [new RuleEngineModelDefault( function: LOG_A_TAG, - args: new RuleEngineModelDefaultArgs(analyticsValue: PBSUtils.randomString))] + args: new RuleEngineModelDefaultArgs(analyticsValue: analyticsValue))] } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -1648,8 +1680,8 @@ class RuleEngineSpec extends ModuleBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -1679,6 +1711,62 @@ class RuleEngineSpec extends ModuleBaseSpec { assert impResult.appliedTo.impIds == bidRequest.imp.id } + def "PBS should log the default model group and shouldn't modify response when other rules not fire"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.device = new Device(geo: new Geo(country: BULGARIA)) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with default model" + def analyticsValue = PBSUtils.randomString + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].modelDefault = [new RuleEngineModelDefault( + function: LOG_A_TAG, + args: new RuleEngineModelDefaultArgs(analyticsValue: analyticsValue))] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE_CODE + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == analyticsValue + assert impResult.values.resultFunction == LOG_A_TAG.value + assert impResult.values.conditionFired == DEFAULT_CONDITIONS + assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + + assert !impResult.values.biddersRemoved + assert !impResult.values.seatNonBid + } + def "PBS shouldn't log the default model group and modify response when rules fire"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { From eb019204919aca82fe2aefa6b767b19b92530958 Mon Sep 17 00:00:00 2001 From: markiian Date: Wed, 17 Sep 2025 18:13:28 +0300 Subject: [PATCH 09/18] Update after review --- .../filter/FilterBiddersFunctionConfig.java | 1 - .../model/config/PbRulesEngine.groovy | 4 +- .../model/config/RuleEngineFunction.groovy | 4 +- .../config/RuleEngineFunctionArgs.groovy | 6 +- .../RuleEngineModelRuleResultsArgs.groovy | 4 + .../{RuleSets.groovy => RuleSet.groovy} | 6 +- .../model/config/RuleVariables.groovy | 39 - .../model/request/auction/Kvps.groovy | 6 - .../model/request/auction/Prebid.groovy | 2 +- .../pbruleengine/RuleEngineAliasSpec.groovy | 289 +++ .../pbruleengine/RuleEngineBaseSpec.groovy | 75 + ...ec.groovy => RuleEngineGeneralSpec.groovy} | 1934 +++++++---------- .../pbruleengine/RuleEngineSyncSpec.groovy | 324 +++ .../richmedia/RichMediaFilterSpec.groovy | 6 - 14 files changed, 1473 insertions(+), 1227 deletions(-) rename src/test/groovy/org/prebid/server/functional/model/config/{RuleSets.groovy => RuleSet.groovy} (87%) delete mode 100644 src/test/groovy/org/prebid/server/functional/model/config/RuleVariables.groovy delete mode 100644 src/test/groovy/org/prebid/server/functional/model/request/auction/Kvps.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy rename src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/{RuleEngineSpec.groovy => RuleEngineGeneralSpec.groovy} (72%) create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy diff --git a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java index 28ba0f5e518..a75fdeb99c1 100644 --- a/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java +++ b/extra/modules/pb-rule-engine/src/main/java/org/prebid/server/hooks/modules/rule/engine/core/request/result/functions/filter/FilterBiddersFunctionConfig.java @@ -19,7 +19,6 @@ public class FilterBiddersFunctionConfig { @JsonProperty("seatnonbid") BidRejectionReason seatNonBid = BidRejectionReason.REQUEST_BLOCKED_OPTIMIZED; - @JsonProperty("ifSyncedId") Boolean ifSyncedId; @JsonProperty("analyticsValue") diff --git a/src/test/groovy/org/prebid/server/functional/model/config/PbRulesEngine.groovy b/src/test/groovy/org/prebid/server/functional/model/config/PbRulesEngine.groovy index 01696cff06d..279f46a0fc3 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/PbRulesEngine.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/PbRulesEngine.groovy @@ -4,13 +4,13 @@ class PbRulesEngine { Boolean enabled Boolean generateRulesFromBidderConfig - List ruleSets + List ruleSets static PbRulesEngine createRulesEngineWithRule(Boolean enabled = true) { new PbRulesEngine().tap { it.enabled = enabled it.generateRulesFromBidderConfig = false - it.ruleSets = [RuleSets.createRuleSets()] + it.ruleSets = [RuleSet.createRuleSets()] } } } diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy index 7f747f73e1c..4b441018858 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy @@ -27,9 +27,7 @@ enum RuleEngineFunction { AD_UNIT_CODE_IN("adUnitCodeIn"), DEVICE_TYPE("deviceType"), DEVICE_TYPE_IN("deviceTypeIn"), - BID_PRICE("bidPrice"), - - IMP_ID("impId") + BID_PRICE("bidPrice") private String value diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy index fbb61c6961b..a2cb809d0fe 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunctionArgs.groovy @@ -1,5 +1,6 @@ package org.prebid.server.functional.model.config +import com.fasterxml.jackson.annotation.JsonProperty import org.prebid.server.functional.util.PBSUtils class RuleEngineFunctionArgs { @@ -8,7 +9,8 @@ class RuleEngineFunctionArgs { List datacenters List sources List sids - Object pct + @JsonProperty("pct") + Object percent Object key List domains List bundles @@ -24,7 +26,7 @@ class RuleEngineFunctionArgs { datacenters = [PBSUtils.randomString] sources = [PBSUtils.randomString] sids = [PBSUtils.randomNumber] - pct = PBSUtils.getRandomNumber(1, 100) + percent = PBSUtils.getRandomNumber(1, 100) key = PBSUtils.randomString domains = [PBSUtils.randomString] bundles = [PBSUtils.randomString] diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy index c5c48a0a630..24e781a467e 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy @@ -1,5 +1,6 @@ package org.prebid.server.functional.model.config +import com.fasterxml.jackson.annotation.JsonProperty import org.prebid.server.functional.model.bidder.BidderName import org.prebid.server.functional.model.response.auction.BidRejectionReason import org.prebid.server.functional.util.PBSUtils @@ -7,8 +8,11 @@ import org.prebid.server.functional.util.PBSUtils class RuleEngineModelRuleResultsArgs { List bidders + @JsonProperty("seatnonbid") BidRejectionReason seatNonBid + @JsonProperty("analyticsValue") String analyticsValue + @JsonProperty("ifSyncedId") Boolean ifSyncedId static RuleEngineModelRuleResultsArgs createRuleEngineModelRuleResultsArgs(BidderName bidderName){ diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleSets.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleSet.groovy similarity index 87% rename from src/test/groovy/org/prebid/server/functional/model/config/RuleSets.groovy rename to src/test/groovy/org/prebid/server/functional/model/config/RuleSet.groovy index 7027b160e28..50c9cbba4ad 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleSets.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleSet.groovy @@ -3,7 +3,7 @@ package org.prebid.server.functional.model.config import static org.prebid.server.functional.model.config.Stage.PROCESSED_AUCTION_REQUEST import static org.prebid.server.functional.util.PBSUtils.randomString -class RuleSets { +class RuleSet { Boolean enabled Stage stage @@ -11,8 +11,8 @@ class RuleSets { String version List modelGroups - static RuleSets createRuleSets() { - new RuleSets().tap { + static RuleSet createRuleSets() { + new RuleSet().tap { it.enabled = true it.stage = PROCESSED_AUCTION_REQUEST it.name = randomString diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleVariables.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleVariables.groovy deleted file mode 100644 index f1a91d2475a..00000000000 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleVariables.groovy +++ /dev/null @@ -1,39 +0,0 @@ -package org.prebid.server.functional.model.config - -import com.fasterxml.jackson.annotation.JsonValue - - -enum RuleVariables { - - DEVICE_COUNTRY("deviceCountry"), - DEVICE_COUNTRY_SET("deviceCountrySet"), - DATACENTER("datacenters"), - CHANNEL("channel"), - BUYER_UID_AVAILABLE("buyeruidAvailable"), - ANY_ID_AVAILABLE("anyIdAvailable"), - ANY_ID_OR_USER_FPD_AVAILABLE("anyIdOrUserFpdAvailable"), - ANY_ID_OR_FPD_AVAIL("anyIdOrFpdAvail"), - GPP_SID("gppSid"), - TCF_IN_SCOPE("tcfInScope"), - PERCENT("percent"), - DOMAIN("domain"), - BUNDLE("bundle"), - MEDIA_TYPES("mediaTypes"), - AD_UNIT_CODE("adUnitCode"), - DEVICE_TYPE("deviceType"), - BID_PRICE("bidPrice"), - MACRO("macro"), - INVALID("invalid") - - private String value - - RuleVariables(String value) { - this.value = value - } - - @Override - @JsonValue - String toString() { - return value - } -} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Kvps.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Kvps.groovy deleted file mode 100644 index 0565f09c72f..00000000000 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/Kvps.groovy +++ /dev/null @@ -1,6 +0,0 @@ -package org.prebid.server.functional.model.request.auction - -class Kvps { - - String anyString -} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy index c79c2a285b5..26f4296a938 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy @@ -45,7 +45,7 @@ class Prebid { PaaFormat paaFormat @JsonProperty("alternatebiddercodes") AlternateBidderCodes alternateBidderCodes - Kvps kvps + Map kvps static class Channel { diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy new file mode 100644 index 00000000000..8cd8dc0cf63 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy @@ -0,0 +1,289 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.bidder.Generic +import org.prebid.server.functional.model.bidder.Openx +import org.prebid.server.functional.model.request.auction.Amx +import org.prebid.server.functional.model.request.auction.Imp + +import static org.prebid.server.functional.model.bidder.BidderName.ALIAS +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.bidder.BidderName.OPENX_ALIAS +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithExcludeResult +import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithIncludeResult +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + +class RuleEngineAliasSpec extends RuleEngineBaseSpec { + + def "PBS should leave only hard alias bidder at imps when hard alias bidder include in account config"() { + given: "Bid request with multiply imps bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(Imp.defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + openxAlias = Openx.defaultOpenx + } + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(OPENX_ALIAS)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED + + and: "Bid response should contain seatBid.seat" + assert bidResponse.seatbid.seat == [OPENX_ALIAS] + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.status == SUCCESS + + and: "Analytics result detail info" + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == [GENERIC, OPENX, AMX] + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should populate seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == [OPENX, AMX, GENERIC].sort() + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS should remove hard alias bidder from imps when hard alias bidder excluded in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(Imp.defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + openxAlias = Openx.defaultOpenx + } + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(OPENX_ALIAS)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX, GENERIC].sort() + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.status == SUCCESS + + and: "Analytics result detail info" + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == [bidRequest.imp[1].id] + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX_ALIAS + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS should leave only soft alias bidder at imps when soft alias bidder include in account config"() { + given: "Bid request with multiply imps bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(Imp.defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + alias = new Generic() + generic = null + amx = new Amx() + openx = Openx.defaultOpenx + } + ext.prebid.aliases = [(ALIAS.value): GENERIC] + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(ALIAS)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seat" + assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED + assert bidResponse.seatbid.seat == [ALIAS] + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.status == SUCCESS + + and: "Analytics result detail info" + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == [OPENX, GENERIC, AMX].sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + def seatNonBid = bidResponse.ext.seatnonbid + assert seatNonBid.seat == [ALIAS] + assert seatNonBid.nonBid[0].impId == [bidRequest.imp[0].id] + assert seatNonBid.nonBid[0].statusCode == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] + } + + def "PBS should remove soft alias bidder from imps when soft alias bidder excluded in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(Imp.defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + alias = new Generic() + generic = null + amx = new Amx() + openx = Openx.defaultOpenx + } + ext.prebid.aliases = [(ALIAS.value): GENERIC] + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(ALIAS)] + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX, GENERIC].sort() + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.status == SUCCESS + + and: "Analytics result detail info" + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved == [ALIAS] + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == [bidRequest.imp[1].id] + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == ALIAS + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy new file mode 100644 index 00000000000..76d9d374cb1 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy @@ -0,0 +1,75 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.bidder.Generic +import org.prebid.server.functional.model.bidder.Openx +import org.prebid.server.functional.model.config.AccountConfig +import org.prebid.server.functional.model.config.AccountHooksConfiguration +import org.prebid.server.functional.model.config.PbRulesEngine +import org.prebid.server.functional.model.config.PbsModulesConfig +import org.prebid.server.functional.model.db.Account +import org.prebid.server.functional.model.request.auction.Amx +import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.Device +import org.prebid.server.functional.model.request.auction.DistributionChannel +import org.prebid.server.functional.model.request.auction.Geo +import org.prebid.server.functional.service.PrebidServerService +import org.prebid.server.functional.tests.module.ModuleBaseSpec +import org.prebid.server.functional.util.PBSUtils + +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.bidder.BidderName.OPENX_ALIAS +import static org.prebid.server.functional.model.pricefloors.Country.USA +import static org.prebid.server.functional.model.request.auction.DistributionChannel.SITE +import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE +import static org.prebid.server.functional.testcontainers.Dependencies.getNetworkServiceContainer + +abstract class RuleEngineBaseSpec extends ModuleBaseSpec { + + protected static final Integer BIDDERS_REQUESTED = 3 + protected static final Integer ONE_BIDDER_REQUESTED = 1 + protected static final String APPLIED_FOR_ALL_IMPS = "*" + protected static final String DEFAULT_CONDITIONS = "default" + protected final static String CALL_METRIC = "modules.module.%s.stage.%s.hook.%s.call" + protected final static String FAILER_METRIC = "modules.module.%s.stage.%s.hook.%s.failure" + protected final static String NOOP_METRIC = "modules.module.%s.stage.%s.hook.%s.success.noop" + protected final static String UPDATE_METRIC = "modules.module.%s.stage.%s.hook.%s.success.update" + protected static final Map OPENX_CONFIG = ["adapters.${OPENX}.enabled" : "true", + "adapters.${OPENX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + protected static final Map AMX_CONFIG = ["adapters.${AMX}.enabled" : "true", + "adapters.${AMX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + protected static final Map OPENX_ALIAS_CONFIG = ["adapters.${OPENX}.aliases.${OPENX_ALIAS}.enabled" : "true", + "adapters.${OPENX}.aliases.${OPENX_ALIAS}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + protected static final String PB_RULE_ENGINE_MODULE_NAME_CODE = "pb-rule-engine" + protected static final String CONFIG_DATA_CENTER = PBSUtils.randomString + protected static final Closure FAILED_PARSE_RULE_ENGINE_CONFIG_MESSAGE = { accountId, function -> + "Failed to parse rule-engine config for account ${accountId}: " + + "Function '${function}' configuration is invalid: No arguments allowed" + } + protected static final PrebidServerService pbsServiceWithRulesEngineModule = pbsServiceFactory.getService( + getRulesEngineSettings() + AMX_CONFIG + OPENX_CONFIG + OPENX_ALIAS_CONFIG + ['datacenter-region': CONFIG_DATA_CENTER]) + + protected static BidRequest getDefaultBidRequestWithMultiplyBidders(DistributionChannel distributionChannel = SITE) { + BidRequest.getDefaultBidRequest(distributionChannel).tap { + it.imp[0].ext.prebid.bidder.amx = new Amx() + it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + it.imp[0].ext.prebid.bidder.generic = new Generic() + } + } + + protected static void updateBidRequestWithGeoCountry(BidRequest bidRequest) { + bidRequest.device = new Device(geo: new Geo(country: USA)) + } + + protected static void updateBidRequestWithTraceVerboseAndReturnAllBidStatus(BidRequest bidRequest) { + bidRequest.ext.prebid.tap { + it.trace = VERBOSE + it.returnAllBidStatus = true + } + } + + protected static getAccountWithRulesEngine(String accountId, PbRulesEngine ruleEngine) { + def accountHooksConfiguration = new AccountHooksConfiguration(modules: new PbsModulesConfig(pbRuleEngine: ruleEngine)) + new Account(uuid: accountId, config: new AccountConfig(hooks: accountHooksConfiguration)) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineGeneralSpec.groovy similarity index 72% rename from src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineGeneralSpec.groovy index 977266f2961..2c5ce7f43cb 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineGeneralSpec.groovy @@ -2,20 +2,14 @@ package org.prebid.server.functional.tests.module.pbruleengine import org.prebid.server.functional.model.ChannelType import org.prebid.server.functional.model.UidsCookie -import org.prebid.server.functional.model.bidder.Generic import org.prebid.server.functional.model.bidder.Openx -import org.prebid.server.functional.model.config.AccountConfig -import org.prebid.server.functional.model.config.AccountHooksConfiguration -import org.prebid.server.functional.model.config.PbRulesEngine -import org.prebid.server.functional.model.config.PbsModulesConfig import org.prebid.server.functional.model.config.RuleEngineFunctionArgs import org.prebid.server.functional.model.config.RuleEngineModelDefault import org.prebid.server.functional.model.config.RuleEngineModelDefaultArgs import org.prebid.server.functional.model.config.RuleEngineModelSchema -import org.prebid.server.functional.model.config.RuleSets +import org.prebid.server.functional.model.config.RuleSet import org.prebid.server.functional.model.config.RulesEngineModelGroups import org.prebid.server.functional.model.config.Stage -import org.prebid.server.functional.model.db.Account import org.prebid.server.functional.model.db.StoredImp import org.prebid.server.functional.model.pricefloors.Country import org.prebid.server.functional.model.pricefloors.MediaType @@ -23,17 +17,14 @@ import org.prebid.server.functional.model.request.GppSectionId import org.prebid.server.functional.model.request.auction.Amx import org.prebid.server.functional.model.request.auction.AppExt import org.prebid.server.functional.model.request.auction.AppExtData -import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.request.auction.Content 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.DeviceType -import org.prebid.server.functional.model.request.auction.DistributionChannel 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.Imp import org.prebid.server.functional.model.request.auction.ImpExtContextData -import org.prebid.server.functional.model.request.auction.Kvps import org.prebid.server.functional.model.request.auction.PrebidStoredRequest import org.prebid.server.functional.model.request.auction.Publisher import org.prebid.server.functional.model.request.auction.Regs @@ -44,24 +35,21 @@ import org.prebid.server.functional.model.request.auction.UserExt import org.prebid.server.functional.model.request.auction.UserExtData import org.prebid.server.functional.model.response.auction.BidRejectionReason import org.prebid.server.functional.model.response.auction.BidResponse -import org.prebid.server.functional.service.PrebidServerService -import org.prebid.server.functional.tests.module.ModuleBaseSpec -import org.prebid.server.functional.util.HttpUtil import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.TcfConsent -import spock.lang.IgnoreRest +import spock.lang.RepeatUntilFailure import java.time.Instant import static java.lang.Boolean.FALSE import static java.lang.Boolean.TRUE import static org.prebid.server.functional.model.ChannelType.WEB -import static org.prebid.server.functional.model.bidder.BidderName.ALIAS +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE import static org.prebid.server.functional.model.bidder.BidderName.AMX import static org.prebid.server.functional.model.bidder.BidderName.GENERIC import static org.prebid.server.functional.model.bidder.BidderName.OPENX -import static org.prebid.server.functional.model.bidder.BidderName.OPENX_ALIAS import static org.prebid.server.functional.model.bidder.BidderName.UNKNOWN +import static org.prebid.server.functional.model.config.ModuleHookImplementation.PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule import static org.prebid.server.functional.model.config.ResultFunction.LOG_A_TAG import static org.prebid.server.functional.model.config.RuleEngineFunction.AD_UNIT_CODE @@ -90,6 +78,7 @@ import static org.prebid.server.functional.model.config.RuleEngineFunction.USER_ import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithExcludeResult import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithIncludeResult import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithLogATagResult +import static org.prebid.server.functional.model.config.Stage.PROCESSED_AUCTION_REQUEST import static org.prebid.server.functional.model.pricefloors.Country.BULGARIA import static org.prebid.server.functional.model.pricefloors.Country.USA import static org.prebid.server.functional.model.pricefloors.MediaType.BANNER @@ -97,30 +86,13 @@ import static org.prebid.server.functional.model.request.auction.DistributionCha import static org.prebid.server.functional.model.request.auction.DistributionChannel.DOOH import static org.prebid.server.functional.model.request.auction.DistributionChannel.SITE import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS -import static org.prebid.server.functional.model.request.auction.Imp.getDefaultImpression import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE import static org.prebid.server.functional.model.response.auction.BidRejectionReason.ERROR_NO_BID import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE -import static org.prebid.server.functional.testcontainers.Dependencies.getNetworkServiceContainer import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID import static org.prebid.server.functional.util.privacy.TcfConsent.PurposeId.BASIC_ADS -class RuleEngineSpec extends ModuleBaseSpec { - - private static final Integer BIDDERS_REQUESTED = 3 - private static final Integer ONE_BIDDER_REQUESTED = 1 - private static final String APPLIED_FOR_ALL_IMPS = "*" - private static final String DEFAULT_CONDITIONS = "default" - private static final Map OPENX_CONFIG = ["adapters.${OPENX}.enabled" : "true", - "adapters.${OPENX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] - private static final Map AMX_CONFIG = ["adapters.${AMX}.enabled" : "true", - "adapters.${AMX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] - private static final Map OPENX_ALIAS_CONFIG = ["adapters.${OPENX}.aliases.${OPENX_ALIAS}.enabled" : "true", - "adapters.${OPENX}.aliases.${OPENX_ALIAS}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] - private static final String PB_RULE_ENGINE_CODE = "pb-rule-engine" - private static final String CONFIG_DATA_CENTER = PBSUtils.randomString - private static final PrebidServerService pbsServiceWithRulesEngineModule = pbsServiceFactory.getService( - getRulesEngineSettings() + AMX_CONFIG + OPENX_CONFIG + OPENX_ALIAS_CONFIG + ['datacenter-region': CONFIG_DATA_CENTER]) +class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS shouldn't remove bidder when rule engine not fully configured in account"() { given: "Bid request with multiply bidders" @@ -136,6 +108,9 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Cache account" pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + and: "Flush metrics" + flushMetrics(pbsServiceWithRulesEngineModule) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -156,17 +131,75 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result shouldn't contain info about rule engine" assert !getAnalyticResults(bidResponse) + and: "PBs should populate call and noop metrics" + def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() + assert metrics[CALL_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] == 1 + assert metrics[NOOP_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] == 1 + + and: "PBs should populate update metrics" + assert !metrics[UPDATE_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] + where: pbRulesEngine << [ createRulesEngineWithRule().tap { it.ruleSets = [] }, createRulesEngineWithRule().tap { it.ruleSets[0].stage = null }, createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema = [] }, createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules = [] }, - createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results = [] }, - createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].conditions = [] }, + createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results = [] } ] } + def "PBS shouldn't remove bidder when rule engine not fully configured in account wihout rule conditions"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with enabled rules engine" + def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].conditions = [] } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + and: "Flush metrics" + flushMetrics(pbsServiceWithRulesEngineModule) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "PBs should populate failer metrics" + def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() + assert metrics[FAILER_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] == 1 + + and: "PBs shouldn't contain noop and call metrics" + assert !metrics[CALL_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] + assert !metrics[NOOP_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] + assert !metrics[UPDATE_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] + + and: "Invocation result should contain warning of rule engine" + assert getInvocationResult(bidResponse)[0].message == "No matching rule found" + } + def "PBS shouldn't remove bidder and emit a warning when args rule engine not fully configured in account"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -182,6 +215,9 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Cache account" pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + and: "Flush metrics" + flushMetrics(pbsServiceWithRulesEngineModule) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -199,8 +235,14 @@ class RuleEngineSpec extends ModuleBaseSpec { assert bidResponse.seatbid.size() == BIDDERS_REQUESTED assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() - and: "Invocation result should contain warning of rule engine" - assert getInvocationResult(bidResponse)[0].message == "Rule for account ${bidRequest.accountId} is not ready" + and: "PBs should populate failer metrics" + def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() + assert metrics[FAILER_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] == 1 + + and: "PBs shouldn't contain noop and call metrics" + assert !metrics[CALL_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] + assert !metrics[NOOP_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] + assert !metrics[UPDATE_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] } def "PBS shouldn't remove bidder and emit a warning when model group rule engine not fully configured in account"() { @@ -308,7 +350,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.errors where: - pbRuleEngine << [null, createRulesEngineWithRule(false)] + pbRuleEngine << [createRulesEngineWithRule(false), null] } def "PBS shouldn't remove bidder when rule sets disabled in account"() { @@ -394,6 +436,9 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Cache account" pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + and: "Flush metric" + flushMetrics(pbsServiceWithRulesEngineModule) + when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -413,6 +458,11 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors + and: "PBs should populate call and update metrics" + def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() + assert metrics[CALL_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] == 1 + assert metrics[UPDATE_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] == 1 + and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] @@ -424,7 +474,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def "PBS should remove bidder from imps and use default 203 value for seatNonBid when seatNonBid null and exclude bidder in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(defaultImpression) + it.imp.add(Imp.defaultImpression) it.imp[1].ext.prebid.bidder.tap { openx = Openx.defaultOpenx amx = new Amx() @@ -457,22 +507,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should populate seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -485,7 +539,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def "PBS should remove bidder from imps and update seatNonBid with other code when seatNonBid override and exclude bidder in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(defaultImpression) + it.imp.add(Imp.defaultImpression) it.imp[1].ext.prebid.bidder.tap { openx = Openx.defaultOpenx amx = new Amx() @@ -519,22 +573,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == bidRejectionReason + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should populate seatNon bid" assert bidResponse.ext.seatnonbid.size() == 1 @@ -547,7 +605,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def "PBS should remove bidder from imps and not update seatNonBid when returnAllBidStatus disabled and exclude bidder in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(defaultImpression) + it.imp.add(Imp.defaultImpression) it.imp[1].ext.prebid.bidder.tap { openx = Openx.defaultOpenx amx = new Amx() @@ -586,25 +644,29 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Response shouldn't populate seatNon bid with code 203" assert !bidResponse.ext.seatnonbid - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == ERROR_NO_BID + impResult.appliedTo.impIds == bidRequest.imp.id + } } - def "PBS should when bidder doesn't specified with account and request"() { + def "PBS shouldn't include unknown bidder when unknown bidder specified in result account"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) @@ -613,8 +675,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [//createRuleEngineModelRuleWithExcludeResult(UNKNOWN), - createRuleEngineModelRuleWithIncludeResult(UNKNOWN)] + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(UNKNOWN)] } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -639,196 +700,16 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext.seatnonbid } - def "PBS should include one bidder and update analytics when multiple bidders specified and one included in account"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with rules sets" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(OPENX)] - } - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED - - and: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE - assert result.status == SUCCESS - def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == [GENERIC, AMX].sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id - - and: "Response should populate seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS should remove bidder by device geo from imps when bidder excluded in account config"() { + def "PBS shouldn't exclude unknown bidder when unknown bidder specified in result account"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(defaultImpression) updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] - } - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == 2 - - and: "Bid response should contain seatBids" - assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() - - and: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE - assert result.status == SUCCESS - def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id - - and: "Response should populate seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS should leave only include bidder at imps when bidder include in account config"() { - given: "Bid request with multiply imps bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - } - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with rules sets" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] - } - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC] - - and: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE - assert result.status == SUCCESS - def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved == [AMX, OPENX] - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id - - and: "Response should populate seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS should only logATag when present only function log a tag"() { - given: "Bid request with multiply imps bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - } - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with rules sets" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithLogATagResult()] + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(UNKNOWN)] } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -850,353 +731,19 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.errors and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE - assert result.status == SUCCESS - def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert !impResult.values.biddersRemoved - assert !impResult.values.seatNonBid - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] - } - - def "PBS should leave only hard alias bidder at imps when hard alias bidder include in account config"() { - given: "Bid request with multiply imps bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - openxAlias = Openx.defaultOpenx - } - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with rules sets" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(OPENX_ALIAS)] - } - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED - - and: "Bid response should contain seatBid.seat" - assert bidResponse.seatbid.seat == [OPENX_ALIAS] - - and: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE - assert result.status == SUCCESS - def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == [GENERIC, OPENX, AMX] - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id - - and: "Response should populate seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == [OPENX, AMX, GENERIC].sort() - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS should remove hard alias bidder from imps when hard alias bidder excluded in account config"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - openxAlias = Openx.defaultOpenx - } - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with rules sets" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(OPENX_ALIAS)] - } - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [OPENX, AMX, GENERIC].sort() - - and: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE - assert result.status == SUCCESS - def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [bidRequest.imp[1].id] - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX_ALIAS - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS should leave only soft alias bidder at imps when soft alias bidder include in account config"() { - given: "Bid request with multiply imps bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - alias = new Generic() - generic = null - amx = new Amx() - openx = Openx.defaultOpenx - } - ext.prebid.aliases = [(ALIAS.value): GENERIC] - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with rules sets" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(ALIAS)] - } - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seat" - assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED - assert bidResponse.seatbid.seat == [ALIAS] - - and: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE - assert result.status == SUCCESS - def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == [OPENX, GENERIC, AMX].sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id - - and: "Response should seatNon bid with code 203" - def seatNonBid = bidResponse.ext.seatnonbid - assert seatNonBid.seat == [ALIAS] - assert seatNonBid.nonBid[0].impId == [bidRequest.imp[0].id] - assert seatNonBid.nonBid[0].statusCode == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] - } - - def "PBS should remove soft alias bidder from imps when soft alias bidder excluded in account config"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(Imp.defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - alias = new Generic() - generic = null - amx = new Amx() - openx = Openx.defaultOpenx - } - ext.prebid.aliases = [(ALIAS.value): GENERIC] - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with rules sets" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(ALIAS)] - } - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [OPENX, AMX, GENERIC].sort() - - and: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE - assert result.status == SUCCESS - def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved == [ALIAS] - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == [bidRequest.imp[1].id] - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == ALIAS - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS should remove bidder and update analytics when first rule sets disabled and second enabled in account config"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with rules sets" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets.first.enabled = false - it.ruleSets.add(RuleSets.createRuleSets()) - } - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain two seat" - assert bidResponse.seatbid.size() == 2 - - and: "Response should contain seat bid" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE - assert result.status == SUCCESS - def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert !bidResponse.ext.seatnonbid } - def "PBS should skip rule set and take next one when rule sets not a processed auction request"() { + def "PBS should include one bidder and update analytics when multiple bidders specified and one included in account"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } - and: "Account with rules engine and several rule sets" + and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC), - createRuleEngineModelRuleWithExcludeResult(AMX), - createRuleEngineModelRuleWithExcludeResult(OPENX)] - it.ruleSets[0].stage = stage as Stage - it.ruleSets.add(RuleSets.createRuleSets()) - it.ruleSets[1].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(AMX)] + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(OPENX)] } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -1207,11 +754,8 @@ class RuleEngineSpec extends ModuleBaseSpec { when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - then: "Bid response should contain two seat" - assert bidResponse.seatbid.size() == 2 - - and: "Response should contain seat bid" - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX].sort() + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -1220,37 +764,39 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == [GENERIC, AMX].sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } - and: "Response should seatNon bid with code 203" + and: "Response should populate seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - - where: - stage << Stage.values() - Stage.PROCESSED_AUCTION_REQUEST } - def "PBS shouldn't remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { + def "PBS should remove bidder by device geo from imps when bidder excluded in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(Imp.defaultImpression) updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } @@ -1258,93 +804,116 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = true } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) - and: "Cookies headers" - def uidsCookie = UidsCookie.defaultUidsCookie - def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) - and: "Cache account" pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.size() == 2 - and: "PBs should perform bidder requests" + and: "Bid response should contain seatBids" + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() + + and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.status == SUCCESS - and: "Analytics result shouldn't contain info about rule engine" - assert !getAnalyticResults(bidResponse) + and: "Analytics result detail info" + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should populate seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } - def "PBS should remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=false in account config"() { - given: "Bid request with multiply bidders" + def "PBS should leave only include bidder at imps when bidder include in account config"() { + given: "Bid request with multiply imps bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(Imp.defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + } updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = false + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) - and: "Cookies headers" - def uidsCookie = UidsCookie.defaultUidsCookie - def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) - and: "Cache account" pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 - assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() + assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC] - and: "PBs should perform bidder requests" + and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved == [AMX, OPENX] + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } - and: "Response should seatNon bid with code 203" + and: "Response should populate seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == GENERIC @@ -1352,17 +921,21 @@ class RuleEngineSpec extends ModuleBaseSpec { assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } - def "PBS shouldn't remove bidder from imps when bidder hasn't ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { - given: "Bid request with multiply bidders" + def "PBS should only logATag when present only function log a tag"() { + given: "Bid request with multiply imps bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + it.imp.add(Imp.defaultImpression) + it.imp[1].ext.prebid.bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + } updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = true + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithLogATagResult()] } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -1374,10 +947,9 @@ class RuleEngineSpec extends ModuleBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.size() == 3 - and: "PBs should perform bidder requests" + and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) and: "PBS should not contain errors, warnings" @@ -1387,11 +959,29 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.status == SUCCESS + + and: "Analytics result detail info" + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + !impResult.values.biddersRemoved + !impResult.values.seatNonBid + impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + } } - def "PBS should remove requested bidders at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=true in account config"() { + def "PBS should remove bidder and update analytics when first rule sets disabled and second enabled in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) @@ -1400,24 +990,23 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = true + it.ruleSets.first.enabled = false + it.ruleSets.add(RuleSet.createRuleSets()) } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) - and: "Cookies headers" - def uidsCookie = UidsCookie.defaultUidsCookie - def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) - and: "Cache account" pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain two seat" + assert bidResponse.seatbid.size() == 2 - then: "Bid response shouldn't contain seat" - assert !bidResponse.seatbid.seat + and: "Response should contain seat bid" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -1426,59 +1015,65 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == [AMX, OPENX, GENERIC].sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == [OPENX, GENERIC] + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } - def "PBS should leave only include bidder at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=false in account config"() { - given: "Bid request with multiply imps bidders" + def "PBS should skip rule set and take next one when rule sets not a processed auction request"() { + given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } - and: "Account with rules sets" + and: "Account with rules engine and several rule sets" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = false + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC), + createRuleEngineModelRuleWithExcludeResult(AMX), + createRuleEngineModelRuleWithExcludeResult(OPENX)] + it.ruleSets[0].stage = stage as Stage + it.ruleSets.add(RuleSet.createRuleSets()) + it.ruleSets[1].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(AMX)] } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) - and: "Cookies headers" - def uidsCookie = UidsCookie.defaultUidsCookie - def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) - and: "Cache account" pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - then: "Bid response should contain seat" - assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED - assert bidResponse.seatbid.seat == [GENERIC] + then: "Bid response should contain two seat" + assert bidResponse.seatbid.size() == 2 + + and: "Response should contain seat bid" + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX].sort() and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -1487,66 +1082,36 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == [OPENX, AMX] - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + def groups = pbRuleEngine.ruleSets[1].modelGroups[0] + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == [OPENX, GENERIC] + assert seatNonBid.seat == GENERIC assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS should leave request bidder at imps when bidder hasn't ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with rules sets" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = true - } - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) + where: + stage << Stage.values() - PROCESSED_AUCTION_REQUEST } def "PBS shouldn't take rule with higher weight and remove bidder when weight negative or zero"() { @@ -1631,22 +1196,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[1] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -1693,22 +1262,26 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } } def "PBS should log the default model group and shouldn't modify response when other rules not fire"() { @@ -1748,23 +1321,27 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == analyticsValue - assert impResult.values.resultFunction == LOG_A_TAG.value - assert impResult.values.conditionFired == DEFAULT_CONDITIONS - assert impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] - - assert !impResult.values.biddersRemoved - assert !impResult.values.seatNonBid + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == analyticsValue + impResult.values.resultFunction == LOG_A_TAG.value + impResult.values.conditionFired == DEFAULT_CONDITIONS + impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + + !impResult.values.biddersRemoved + !impResult.values.seatNonBid + } } def "PBS shouldn't log the default model group and modify response when rules fire"() { @@ -1802,22 +1379,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -1895,7 +1476,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -1916,22 +1497,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -1956,7 +1541,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2001,7 +1586,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2048,7 +1633,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2069,22 +1654,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -2108,7 +1697,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2150,7 +1739,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2171,22 +1760,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -2210,7 +1803,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2252,7 +1845,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2273,22 +1866,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -2312,7 +1909,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2352,7 +1949,7 @@ class RuleEngineSpec extends ModuleBaseSpec { it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: EID_AVAILABLE)] } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2373,22 +1970,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -2413,7 +2014,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2462,7 +2063,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2507,7 +2108,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2528,22 +2129,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -2568,7 +2173,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2608,7 +2213,7 @@ class RuleEngineSpec extends ModuleBaseSpec { it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: USER_FPD_AVAILABLE)] } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2629,23 +2234,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id - + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] @@ -2671,7 +2279,7 @@ class RuleEngineSpec extends ModuleBaseSpec { it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: USER_FPD_AVAILABLE)] } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2699,7 +2307,7 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !getAnalyticResults(bidResponse) where: - requestedUfpUser << [new User(data: null), new User(data: [null]), //todo empty + requestedUfpUser << [new User(data: null), new User(data: [null]), new User(ext: new UserExt(data: null)), new User(data: null, ext: new UserExt(data: null)) ] @@ -2732,22 +2340,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -2791,7 +2403,7 @@ class RuleEngineSpec extends ModuleBaseSpec { it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: FPD_AVAILABLE)] } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2830,7 +2442,7 @@ class RuleEngineSpec extends ModuleBaseSpec { }, getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - site.content = new Content(data: [null]) //todo PLEASE TAKE A LOOK + site.content = new Content(data: [null]) site.ext = new SiteExt(data: null) }, getDefaultBidRequestWithMultiplyBidders().tap { @@ -2839,7 +2451,7 @@ class RuleEngineSpec extends ModuleBaseSpec { }, getDefaultBidRequestWithMultiplyBidders(APP).tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - app.content = new Content(data: [null]) //todo PLEASE TAKE A LOOK + app.content = new Content(data: [null]) app.ext = new AppExt(data: null) }, getDefaultBidRequestWithMultiplyBidders(APP).tap { @@ -2882,22 +2494,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -2919,7 +2535,7 @@ class RuleEngineSpec extends ModuleBaseSpec { it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: GPP_SID_AVAILABLE)] } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -2968,7 +2584,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -3035,22 +2651,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -3144,22 +2764,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -3239,11 +2863,11 @@ class RuleEngineSpec extends ModuleBaseSpec { def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { it.function = PERCENT - it.args = new RuleEngineFunctionArgs(pct: PBSUtils.randomString) + it.args = new RuleEngineFunctionArgs(percent: PBSUtils.randomString) } } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -3285,7 +2909,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { it.function = PERCENT - it.args = new RuleEngineFunctionArgs(pct: percent as Integer) + it.args = new RuleEngineFunctionArgs(percent: percent as Integer) } } @@ -3310,22 +2934,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -3348,7 +2976,7 @@ class RuleEngineSpec extends ModuleBaseSpec { def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { it.function = PERCENT - it.args = new RuleEngineFunctionArgs(pct: percent) + it.args = new RuleEngineFunctionArgs(percent: percent) } } @@ -3400,7 +3028,7 @@ class RuleEngineSpec extends ModuleBaseSpec { } } - and: "Account with disabled or without rules engine" + and: "Account with rules engine" def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -3434,19 +3062,20 @@ class RuleEngineSpec extends ModuleBaseSpec { def "PBS should exclude bidder when prebidKey match with condition"() { given: "Default bid request with multiply bidder" - def key = PBSUtils.randomString + def keyField = "key" + def keyString = PBSUtils.randomString def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - ext.prebid.kvps = new Kvps(anyString: key) + ext.prebid.kvps = [(keyString): keyField] } and: "Create rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { it.function = PREBID_KEY - it.args = new RuleEngineFunctionArgs(key: "anyString") + it.args = new RuleEngineFunctionArgs((keyField): keyString) } - it.ruleSets[0].modelGroups[0].rules[0].conditions = [key] + it.ruleSets[0].modelGroups[0].rules[0].conditions = [keyField] } and: "Save account with rule engine config" @@ -3470,22 +3099,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -3497,16 +3130,17 @@ class RuleEngineSpec extends ModuleBaseSpec { def "PBS shouldn't exclude bidder when prebidKey not match with condition"() { given: "Default bid request with multiply bidder" + def key = PBSUtils.randomString def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - ext.prebid.kvps = new Kvps(anyString: PBSUtils.randomString) + ext.prebid.kvps = [(key): PBSUtils.randomString] } and: "Create rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { it.function = PREBID_KEY - it.args = new RuleEngineFunctionArgs(key: "anyString") + it.args = new RuleEngineFunctionArgs(key: key) } it.ruleSets[0].modelGroups[0].rules[0].conditions = [PBSUtils.randomString] } @@ -3570,22 +3204,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -3597,29 +3235,20 @@ class RuleEngineSpec extends ModuleBaseSpec { where: distributionChannel | bidRequestClosure SITE | { String domain -> - BidRequest.getDefaultBidRequest(distributionChannel).tap { + getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) - it.imp[0].ext.prebid.bidder.amx = new Amx() - it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[0].ext.prebid.bidder.generic = new Generic() updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } } APP | { String domain -> - BidRequest.getDefaultBidRequest(distributionChannel).tap { + getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) - it.imp[0].ext.prebid.bidder.amx = new Amx() - it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[0].ext.prebid.bidder.generic = new Generic() updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } } DOOH | { String domain -> - BidRequest.getDefaultBidRequest(distributionChannel).tap { + getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) - it.imp[0].ext.prebid.bidder.amx = new Amx() - it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[0].ext.prebid.bidder.generic = new Generic() updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } } @@ -3665,12 +3294,15 @@ class RuleEngineSpec extends ModuleBaseSpec { bidRequest << [ getDefaultBidRequestWithMultiplyBidders().tap { it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) }, getDefaultBidRequestWithMultiplyBidders(APP).tap { it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) }, getDefaultBidRequestWithMultiplyBidders(DOOH).tap { it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) }] } @@ -3725,51 +3357,39 @@ class RuleEngineSpec extends ModuleBaseSpec { where: distributionChannel | bidRequestClosure SITE | { String domain -> - BidRequest.getDefaultBidRequest(distributionChannel).tap { + getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - it.imp[0].ext.prebid.bidder.amx = new Amx() - it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[0].ext.prebid.bidder.generic = new Generic() + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } } SITE | { String domain -> - BidRequest.getDefaultBidRequest(distributionChannel).tap { + getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { it.site.domain = domain - it.imp[0].ext.prebid.bidder.amx = new Amx() - it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[0].ext.prebid.bidder.generic = new Generic() + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } } APP | { String domain -> - BidRequest.getDefaultBidRequest(distributionChannel).tap { + getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - it.imp[0].ext.prebid.bidder.amx = new Amx() - it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[0].ext.prebid.bidder.generic = new Generic() + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } } APP | { String domain -> - BidRequest.getDefaultBidRequest(distributionChannel).tap { + getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { it.app.domain = domain - it.imp[0].ext.prebid.bidder.amx = new Amx() - it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[0].ext.prebid.bidder.generic = new Generic() + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } } DOOH | { String domain -> - BidRequest.getDefaultBidRequest(distributionChannel).tap { + getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - it.imp[0].ext.prebid.bidder.amx = new Amx() - it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[0].ext.prebid.bidder.generic = new Generic() + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } } DOOH | { String domain -> - BidRequest.getDefaultBidRequest(distributionChannel).tap { + getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { it.dooh.domain = domain - it.imp[0].ext.prebid.bidder.amx = new Amx() - it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[0].ext.prebid.bidder.generic = new Generic() + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } } } @@ -3808,22 +3428,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -3835,61 +3459,38 @@ class RuleEngineSpec extends ModuleBaseSpec { where: distributionChannel | bidRequestClosure SITE | { String domain -> - BidRequest.getDefaultBidRequest(distributionChannel).tap { + getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) - it.imp[0].ext.prebid.bidder.amx = new Amx() - it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[0].ext.prebid.bidder.generic = new Generic() updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } } SITE | { String domain -> - BidRequest.getDefaultBidRequest(distributionChannel).tap { + getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { it.site.domain = domain - it.imp[0].ext.prebid.bidder.amx = new Amx() - it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[0].ext.prebid.bidder.generic = new Generic() updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } } APP | { String domain -> - BidRequest.getDefaultBidRequest(distributionChannel).tap { + getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) - it.imp[0].ext.prebid.bidder.amx = new Amx() - it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[0].ext.prebid.bidder.generic = new Generic() updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } } APP | { String domain -> - BidRequest.getDefaultBidRequest(distributionChannel).tap { + getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { it.app.domain = domain - it.imp[0].ext.prebid.bidder.amx = new Amx() - it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[0].ext.prebid.bidder.generic = new Generic() updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } } DOOH | { String domain -> - BidRequest.getDefaultBidRequest(distributionChannel).tap { + getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) - it.imp[0].ext.prebid.bidder.amx = new Amx() - it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[0].ext.prebid.bidder.generic = new Generic() updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } } DOOH | { String domain -> - BidRequest.getDefaultBidRequest(distributionChannel).tap { + getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { it.dooh.domain = domain - it.imp[0].ext.prebid.bidder.amx = new Amx() - it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[0].ext.prebid.bidder.generic = new Generic() updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } } @@ -3997,22 +3598,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -4149,22 +3754,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -4305,22 +3914,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -4410,22 +4023,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -4520,7 +4137,7 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Save storedImp into DB" def storedImp = StoredImp.getStoredImp(bidRequest).tap { - impData = getDefaultImpression() + impData = Imp.getDefaultImpression() } storedImpDao.save(storedImp) @@ -4602,22 +4219,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -4743,22 +4364,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -4770,16 +4395,17 @@ class RuleEngineSpec extends ModuleBaseSpec { def "PBS shouldn't exclude bidder when deviceType not match with condition"() { given: "Default bid request with multiply bidders" + def requestDeviceType = PBSUtils.getRandomEnum(DeviceType) def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - device = new Device(devicetype: PBSUtils.getRandomEnum(DeviceType)) + device = new Device(devicetype: requestDeviceType) } and: "Create account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].tap { schema = [new RuleEngineModelSchema(function: DEVICE_TYPE)] - rules[0].conditions = [PBSUtils.getRandomEnum(DeviceType).value as String] + rules[0].conditions = [PBSUtils.getRandomEnum(DeviceType, [requestDeviceType]).value as String] } } @@ -4898,22 +4524,26 @@ class RuleEngineSpec extends ModuleBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" + and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_CODE + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE assert result.status == SUCCESS + + and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -4965,28 +4595,4 @@ class RuleEngineSpec extends ModuleBaseSpec { and: "Analytics result shouldn't contain info about module exclude" assert !getAnalyticResults(bidResponse) } - - private static BidRequest getDefaultBidRequestWithMultiplyBidders(DistributionChannel distributionChannel = SITE) { - BidRequest.getDefaultBidRequest(distributionChannel).tap { - it.imp[0].ext.prebid.bidder.amx = new Amx() - it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx - it.imp[0].ext.prebid.bidder.generic = new Generic() - } - } - - private static void updateBidRequestWithGeoCountry(BidRequest bidRequest) { - bidRequest.device = new Device(geo: new Geo(country: USA)) - } - - private static void updateBidRequestWithTraceVerboseAndReturnAllBidStatus(BidRequest bidRequest) { - bidRequest.ext.prebid.tap { - it.trace = VERBOSE - it.returnAllBidStatus = true - } - } - - private static getAccountWithRulesEngine(String accountId, PbRulesEngine ruleEngine) { - def accountHooksConfiguration = new AccountHooksConfiguration(modules: new PbsModulesConfig(pbRuleEngine: ruleEngine)) - new Account(uuid: accountId, config: new AccountConfig(hooks: accountHooksConfiguration)) - } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy new file mode 100644 index 00000000000..5347136fec3 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy @@ -0,0 +1,324 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.UidsCookie +import org.prebid.server.functional.util.HttpUtil + +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithExcludeResult +import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithIncludeResult +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + +class RuleEngineSyncSpec extends RuleEngineBaseSpec { + + def "PBS shouldn't remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = true + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cookies headers" + def uidsCookie = UidsCookie.defaultUidsCookie + def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=false in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = false + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cookies headers" + def uidsCookie = UidsCookie.defaultUidsCookie + def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.status == SUCCESS + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + assert impResult.status == SUCCESS + assert impResult.values.analyticsKey == groups.analyticsKey + assert impResult.values.modelVersion == groups.version + assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + assert impResult.values.resultFunction == groups.rules.first.results.first.function.value + assert impResult.values.conditionFired == groups.rules.first.conditions.first + assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert impResult.appliedTo.impIds == bidRequest.imp.id + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't remove bidder from imps when bidder hasn't ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = true + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should remove requested bidders at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=true in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = true + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cookies headers" + def uidsCookie = UidsCookie.defaultUidsCookie + def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) + + then: "Bid response shouldn't contain seat" + assert !bidResponse.seatbid.seat + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.status == SUCCESS + + and: "Analytics result detail info" + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == [AMX, OPENX, GENERIC].sort() + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == [OPENX, GENERIC] + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS should leave only include bidder at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=false in account config"() { + given: "Bid request with multiply imps bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = false + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cookies headers" + def uidsCookie = UidsCookie.defaultUidsCookie + def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) + + then: "Bid response should contain seat" + assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED + assert bidResponse.seatbid.seat == [GENERIC] + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.status == SUCCESS + + and: "Analytics result detail info" + def impResult = result.results[0] + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll { + impResult.status == SUCCESS + impResult.values.analyticsKey == groups.analyticsKey + impResult.values.modelVersion == groups.version + impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + impResult.values.resultFunction == groups.rules.first.results.first.function.value + impResult.values.conditionFired == groups.rules.first.conditions.first + impResult.values.biddersRemoved.sort() == [OPENX, AMX] + impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + impResult.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == [OPENX, GENERIC] + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS should leave request bidder at imps when bidder hasn't ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] + it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = true + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/richmedia/RichMediaFilterSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/richmedia/RichMediaFilterSpec.groovy index 9bcc6b7d62a..517da393668 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/richmedia/RichMediaFilterSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/richmedia/RichMediaFilterSpec.groovy @@ -453,10 +453,4 @@ class RichMediaFilterSpec extends ModuleBaseSpec { where: admValue << [PATTERN_NAME, "${PBSUtils.randomString}-${PATTERN_NAME}", "${PATTERN_NAME}.${PBSUtils.randomString}"] } - - static List getAnalyticResults(BidResponse response) { - response.ext.prebid.modules?.trace?.stages?.first() - ?.outcomes?.first()?.groups?.first() - ?.invocationResults?.first()?.analyticsTags?.activities - } } From 2a771df8ac4df7e43b8189736e8caa893be0a7ed Mon Sep 17 00:00:00 2001 From: markiian Date: Wed, 17 Sep 2025 18:15:05 +0300 Subject: [PATCH 10/18] Roll back launch container to false --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4068bc7298f..562b26f520a 100644 --- a/pom.xml +++ b/pom.xml @@ -373,7 +373,7 @@ ${maven-surefire-plugin.version} - true + false ${skipUnitTests} From 30b03da2d1e59bdd8e5c4510c7f76f281d43e68b Mon Sep 17 00:00:00 2001 From: markiian Date: Wed, 17 Sep 2025 18:16:18 +0300 Subject: [PATCH 11/18] Clean up redundant imports --- .../tests/module/pbruleengine/RuleEngineGeneralSpec.groovy | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineGeneralSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineGeneralSpec.groovy index 2c5ce7f43cb..ed0d91f4e04 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineGeneralSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineGeneralSpec.groovy @@ -1,7 +1,6 @@ package org.prebid.server.functional.tests.module.pbruleengine import org.prebid.server.functional.model.ChannelType -import org.prebid.server.functional.model.UidsCookie import org.prebid.server.functional.model.bidder.Openx import org.prebid.server.functional.model.config.RuleEngineFunctionArgs import org.prebid.server.functional.model.config.RuleEngineModelDefault @@ -37,7 +36,6 @@ import org.prebid.server.functional.model.response.auction.BidRejectionReason import org.prebid.server.functional.model.response.auction.BidResponse import org.prebid.server.functional.util.PBSUtils import org.prebid.server.functional.util.privacy.TcfConsent -import spock.lang.RepeatUntilFailure import java.time.Instant From 46634fcd0eda6490950148d43b17118d793b6ef0 Mon Sep 17 00:00:00 2001 From: markiian Date: Thu, 18 Sep 2025 13:47:43 +0300 Subject: [PATCH 12/18] Minor update --- .../tests/module/pbruleengine/RuleEngineGeneralSpec.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineGeneralSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineGeneralSpec.groovy index ed0d91f4e04..ad291e095a7 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineGeneralSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineGeneralSpec.groovy @@ -1613,7 +1613,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Logs should contain error" def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) - assert getLogsByText(logs, "Function 'dataCenterIn' configuration is invalid: " + + assert getLogsByText(logs, "Function '${DATA_CENTER_IN}' configuration is invalid: " + "Field 'datacenters' is required and has to be an array of strings") } From 091792ab759ed37a676e0079be23fe06ac46bfaa Mon Sep 17 00:00:00 2001 From: markiian Date: Thu, 25 Sep 2025 13:51:09 +0300 Subject: [PATCH 13/18] Update after review --- .../model/config/RuleEngineFunction.groovy | 1 - .../functional/model/config/RuleSet.groovy | 4 +- ...ps.groovy => RulesEngineModelGroup.groovy} | 6 +- .../model/request/auction/Prebid.groovy | 3 +- .../pbruleengine/RuleEngineAliasSpec.groovy | 75 +- .../pbruleengine/RuleEngineBaseSpec.groovy | 41 +- ...lSpec.groovy => RuleEngineCoreSpec.groovy} | 1672 ++++++----------- .../pbruleengine/RuleEngineSyncSpec.groovy | 94 +- .../RuleEngineValidationSpec.groovy | 323 ++++ 9 files changed, 993 insertions(+), 1226 deletions(-) rename src/test/groovy/org/prebid/server/functional/model/config/{RulesEngineModelGroups.groovy => RulesEngineModelGroup.groovy} (86%) rename src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/{RuleEngineGeneralSpec.groovy => RuleEngineCoreSpec.groovy} (69%) create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy index 4b441018858..2bd58fa3be2 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy @@ -27,7 +27,6 @@ enum RuleEngineFunction { AD_UNIT_CODE_IN("adUnitCodeIn"), DEVICE_TYPE("deviceType"), DEVICE_TYPE_IN("deviceTypeIn"), - BID_PRICE("bidPrice") private String value diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleSet.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleSet.groovy index 50c9cbba4ad..32fd4f22828 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleSet.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleSet.groovy @@ -9,7 +9,7 @@ class RuleSet { Stage stage String name String version - List modelGroups + List modelGroups static RuleSet createRuleSets() { new RuleSet().tap { @@ -17,7 +17,7 @@ class RuleSet { it.stage = PROCESSED_AUCTION_REQUEST it.name = randomString it.version = randomString - it.modelGroups = [RulesEngineModelGroups.createRulesModuleGroup()] + it.modelGroups = [RulesEngineModelGroup.createRulesModuleGroup()] } } } diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroups.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroup.groovy similarity index 86% rename from src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroups.groovy rename to src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroup.groovy index 8d0565aa21e..b4021e87f21 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroups.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroup.groovy @@ -6,7 +6,7 @@ import org.prebid.server.functional.util.PBSUtils import static org.prebid.server.functional.model.config.RuleEngineModelRule.createRuleEngineModelRule import static org.prebid.server.functional.model.config.RuleEngineModelSchema.createDeviceCountryInSchema -class RulesEngineModelGroups { +class RulesEngineModelGroup { Integer weight String version @@ -16,8 +16,8 @@ class RulesEngineModelGroups { List modelDefault List rules - static RulesEngineModelGroups createRulesModuleGroup() { - new RulesEngineModelGroups().tap { + static RulesEngineModelGroup createRulesModuleGroup() { + new RulesEngineModelGroup().tap { it.weight = PBSUtils.getRandomNumber(0, 100) it.version = PBSUtils.randomString it.analyticsKey = PBSUtils.randomString diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy index 26f4296a938..9848b66c40e 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy @@ -45,7 +45,8 @@ class Prebid { PaaFormat paaFormat @JsonProperty("alternatebiddercodes") AlternateBidderCodes alternateBidderCodes - Map kvps + @JsonProperty("kvps") + Map keyValuePairs static class Channel { diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy index 8cd8dc0cf63..be9c112cfdc 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy @@ -1,8 +1,10 @@ package org.prebid.server.functional.tests.module.pbruleengine +import org.prebid.server.functional.model.ModuleName import org.prebid.server.functional.model.bidder.Generic import org.prebid.server.functional.model.bidder.Openx import org.prebid.server.functional.model.request.auction.Amx +import org.prebid.server.functional.model.request.auction.Bidder import org.prebid.server.functional.model.request.auction.Imp import static org.prebid.server.functional.model.bidder.BidderName.ALIAS @@ -23,12 +25,9 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { it.imp.add(Imp.defaultImpression) it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - openxAlias = Openx.defaultOpenx + updateImpWithOpenXAndAmxAndOpenXAliasBidder(it) } updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -45,7 +44,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED + assert bidResponse.seatbid.size() == 1 and: "Bid response should contain seatBid.seat" assert bidResponse.seatbid.seat == [OPENX_ALIAS] @@ -60,13 +59,13 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { + verifyAll(result.results[0]) { impResult.status == SUCCESS impResult.values.analyticsKey == groups.analyticsKey impResult.values.modelVersion == groups.version @@ -91,12 +90,9 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { it.imp.add(Imp.defaultImpression) it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - openxAlias = Openx.defaultOpenx + updateImpWithOpenXAndAmxAndOpenXAliasBidder(it) } updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -113,8 +109,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [OPENX, AMX, GENERIC].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -126,13 +121,13 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { + verifyAll(result.results[0]) { impResult.status == SUCCESS impResult.values.analyticsKey == groups.analyticsKey impResult.values.modelVersion == groups.version @@ -157,14 +152,10 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { it.imp.add(Imp.defaultImpression) it.imp[1].ext.prebid.bidder.tap { - alias = new Generic() - generic = null - amx = new Amx() - openx = Openx.defaultOpenx + getAliasAndAmxAndOpenXAndWithoutGenericBidder(it) } ext.prebid.aliases = [(ALIAS.value): GENERIC] updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -181,7 +172,6 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seat" - assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED assert bidResponse.seatbid.seat == [ALIAS] and: "PBs should perform bidder request" @@ -194,13 +184,13 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { + verifyAll(result.results[0]) { impResult.status == SUCCESS impResult.values.analyticsKey == groups.analyticsKey impResult.values.modelVersion == groups.version @@ -213,10 +203,11 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { } and: "Response should seatNon bid with code 203" - def seatNonBid = bidResponse.ext.seatnonbid - assert seatNonBid.seat == [ALIAS] - assert seatNonBid.nonBid[0].impId == [bidRequest.imp[0].id] - assert seatNonBid.nonBid[0].statusCode == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == ALIAS + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS should remove soft alias bidder from imps when soft alias bidder excluded in account config"() { @@ -224,14 +215,10 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { it.imp.add(Imp.defaultImpression) it.imp[1].ext.prebid.bidder.tap { - alias = new Generic() - generic = null - amx = new Amx() - openx = Openx.defaultOpenx + getAliasAndAmxAndOpenXAndWithoutGenericBidder(it) } ext.prebid.aliases = [(ALIAS.value): GENERIC] updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -248,8 +235,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [OPENX, AMX, GENERIC].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -261,13 +247,13 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { + verifyAll(result.results[0]) { impResult.status == SUCCESS impResult.values.analyticsKey == groups.analyticsKey impResult.values.modelVersion == groups.version @@ -286,4 +272,21 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } + + private static void updateImpWithOpenXAndAmxAndOpenXAliasBidder(Bidder bidder) { + bidder.tap { + openx = Openx.defaultOpenx + amx = new Amx() + openxAlias = Openx.defaultOpenx + } + } + + private static void getAliasAndAmxAndOpenXAndWithoutGenericBidder(Bidder bidder) { + bidder.tap { + alias = new Generic() + generic = null + amx = new Amx() + openx = Openx.defaultOpenx + } + } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy index 76d9d374cb1..d4b52d38803 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy @@ -1,5 +1,6 @@ package org.prebid.server.functional.tests.module.pbruleengine +import org.prebid.server.functional.model.bidder.BidderName import org.prebid.server.functional.model.bidder.Generic import org.prebid.server.functional.model.bidder.Openx import org.prebid.server.functional.model.config.AccountConfig @@ -7,6 +8,7 @@ import org.prebid.server.functional.model.config.AccountHooksConfiguration import org.prebid.server.functional.model.config.PbRulesEngine import org.prebid.server.functional.model.config.PbsModulesConfig import org.prebid.server.functional.model.db.Account +import org.prebid.server.functional.model.pricefloors.Country import org.prebid.server.functional.model.request.auction.Amx import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.request.auction.Device @@ -16,9 +18,13 @@ import org.prebid.server.functional.service.PrebidServerService import org.prebid.server.functional.tests.module.ModuleBaseSpec import org.prebid.server.functional.util.PBSUtils +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC import static org.prebid.server.functional.model.bidder.BidderName.OPENX import static org.prebid.server.functional.model.bidder.BidderName.OPENX_ALIAS +import static org.prebid.server.functional.model.config.ModuleHookImplementation.PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST +import static org.prebid.server.functional.model.config.Stage.PROCESSED_AUCTION_REQUEST import static org.prebid.server.functional.model.pricefloors.Country.USA import static org.prebid.server.functional.model.request.auction.DistributionChannel.SITE import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE @@ -26,26 +32,20 @@ import static org.prebid.server.functional.testcontainers.Dependencies.getNetwor abstract class RuleEngineBaseSpec extends ModuleBaseSpec { - protected static final Integer BIDDERS_REQUESTED = 3 - protected static final Integer ONE_BIDDER_REQUESTED = 1 + protected static final List MULTI_BID_ADAPTERS = [GENERIC, OPENX, AMX].sort() protected static final String APPLIED_FOR_ALL_IMPS = "*" protected static final String DEFAULT_CONDITIONS = "default" - protected final static String CALL_METRIC = "modules.module.%s.stage.%s.hook.%s.call" - protected final static String FAILER_METRIC = "modules.module.%s.stage.%s.hook.%s.failure" - protected final static String NOOP_METRIC = "modules.module.%s.stage.%s.hook.%s.success.noop" - protected final static String UPDATE_METRIC = "modules.module.%s.stage.%s.hook.%s.success.update" + protected final static String CALL_METRIC = "modules.module.${PB_RULE_ENGINE.code}.stage.${PROCESSED_AUCTION_REQUEST.metricValue}.hook.${PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code}.call" + protected final static String FAILER_METRIC = "modules.module.${PB_RULE_ENGINE.code}.stage.${PROCESSED_AUCTION_REQUEST.metricValue}.hook.${PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code}.failure" + protected final static String NOOP_METRIC = "modules.module.${PB_RULE_ENGINE.code}.stage.${PROCESSED_AUCTION_REQUEST.metricValue}.hook.${PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code}.success.noop" + protected final static String UPDATE_METRIC = "modules.module.${PB_RULE_ENGINE.code}.stage.${PROCESSED_AUCTION_REQUEST.metricValue}.hook.${PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code}.success.update" protected static final Map OPENX_CONFIG = ["adapters.${OPENX}.enabled" : "true", - "adapters.${OPENX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + "adapters.${OPENX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] protected static final Map AMX_CONFIG = ["adapters.${AMX}.enabled" : "true", - "adapters.${AMX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + "adapters.${AMX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] protected static final Map OPENX_ALIAS_CONFIG = ["adapters.${OPENX}.aliases.${OPENX_ALIAS}.enabled" : "true", - "adapters.${OPENX}.aliases.${OPENX_ALIAS}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] - protected static final String PB_RULE_ENGINE_MODULE_NAME_CODE = "pb-rule-engine" + "adapters.${OPENX}.aliases.${OPENX_ALIAS}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] protected static final String CONFIG_DATA_CENTER = PBSUtils.randomString - protected static final Closure FAILED_PARSE_RULE_ENGINE_CONFIG_MESSAGE = { accountId, function -> - "Failed to parse rule-engine config for account ${accountId}: " + - "Function '${function}' configuration is invalid: No arguments allowed" - } protected static final PrebidServerService pbsServiceWithRulesEngineModule = pbsServiceFactory.getService( getRulesEngineSettings() + AMX_CONFIG + OPENX_CONFIG + OPENX_ALIAS_CONFIG + ['datacenter-region': CONFIG_DATA_CENTER]) @@ -54,18 +54,13 @@ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { it.imp[0].ext.prebid.bidder.amx = new Amx() it.imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx it.imp[0].ext.prebid.bidder.generic = new Generic() + it.ext.prebid.trace = VERBOSE + it.ext.prebid.returnAllBidStatus = true } } - protected static void updateBidRequestWithGeoCountry(BidRequest bidRequest) { - bidRequest.device = new Device(geo: new Geo(country: USA)) - } - - protected static void updateBidRequestWithTraceVerboseAndReturnAllBidStatus(BidRequest bidRequest) { - bidRequest.ext.prebid.tap { - it.trace = VERBOSE - it.returnAllBidStatus = true - } + protected static void updateBidRequestWithGeoCountry(BidRequest bidRequest, Country country = USA) { + bidRequest.device = new Device(geo: new Geo(country: country)) } protected static getAccountWithRulesEngine(String accountId, PbRulesEngine ruleEngine) { diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineGeneralSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy similarity index 69% rename from src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineGeneralSpec.groovy rename to src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy index ad291e095a7..db95fa959f5 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineGeneralSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy @@ -1,13 +1,14 @@ package org.prebid.server.functional.tests.module.pbruleengine import org.prebid.server.functional.model.ChannelType +import org.prebid.server.functional.model.ModuleName import org.prebid.server.functional.model.bidder.Openx import org.prebid.server.functional.model.config.RuleEngineFunctionArgs import org.prebid.server.functional.model.config.RuleEngineModelDefault import org.prebid.server.functional.model.config.RuleEngineModelDefaultArgs import org.prebid.server.functional.model.config.RuleEngineModelSchema import org.prebid.server.functional.model.config.RuleSet -import org.prebid.server.functional.model.config.RulesEngineModelGroups +import org.prebid.server.functional.model.config.RulesEngineModelGroup import org.prebid.server.functional.model.config.Stage import org.prebid.server.functional.model.db.StoredImp import org.prebid.server.functional.model.pricefloors.Country @@ -16,12 +17,13 @@ import org.prebid.server.functional.model.request.GppSectionId import org.prebid.server.functional.model.request.auction.Amx import org.prebid.server.functional.model.request.auction.AppExt import org.prebid.server.functional.model.request.auction.AppExtData +import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.request.auction.Content 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.DeviceType +import org.prebid.server.functional.model.request.auction.DistributionChannel 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.Imp import org.prebid.server.functional.model.request.auction.ImpExtContextData import org.prebid.server.functional.model.request.auction.PrebidStoredRequest @@ -42,12 +44,10 @@ import java.time.Instant import static java.lang.Boolean.FALSE import static java.lang.Boolean.TRUE import static org.prebid.server.functional.model.ChannelType.WEB -import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE import static org.prebid.server.functional.model.bidder.BidderName.AMX import static org.prebid.server.functional.model.bidder.BidderName.GENERIC import static org.prebid.server.functional.model.bidder.BidderName.OPENX import static org.prebid.server.functional.model.bidder.BidderName.UNKNOWN -import static org.prebid.server.functional.model.config.ModuleHookImplementation.PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule import static org.prebid.server.functional.model.config.ResultFunction.LOG_A_TAG import static org.prebid.server.functional.model.config.RuleEngineFunction.AD_UNIT_CODE @@ -90,338 +90,12 @@ import static org.prebid.server.functional.model.response.auction.BidRejectionRe import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID import static org.prebid.server.functional.util.privacy.TcfConsent.PurposeId.BASIC_ADS -class RuleEngineGeneralSpec extends RuleEngineBaseSpec { - - def "PBS shouldn't remove bidder when rule engine not fully configured in account"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with enabled rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), pbRulesEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - and: "Flush metrics" - flushMetrics(pbsServiceWithRulesEngineModule) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() - - and: "Analytics result shouldn't contain info about rule engine" - assert !getAnalyticResults(bidResponse) - - and: "PBs should populate call and noop metrics" - def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() - assert metrics[CALL_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] == 1 - assert metrics[NOOP_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] == 1 - - and: "PBs should populate update metrics" - assert !metrics[UPDATE_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] - - where: - pbRulesEngine << [ - createRulesEngineWithRule().tap { it.ruleSets = [] }, - createRulesEngineWithRule().tap { it.ruleSets[0].stage = null }, - createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema = [] }, - createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules = [] }, - createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results = [] } - ] - } - - def "PBS shouldn't remove bidder when rule engine not fully configured in account wihout rule conditions"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with enabled rules engine" - def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].conditions = [] } - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - and: "Flush metrics" - flushMetrics(pbsServiceWithRulesEngineModule) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() - - and: "Analytics result shouldn't contain info about rule engine" - assert !getAnalyticResults(bidResponse) - - and: "PBs should populate failer metrics" - def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() - assert metrics[FAILER_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] == 1 - - and: "PBs shouldn't contain noop and call metrics" - assert !metrics[CALL_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] - assert !metrics[NOOP_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] - assert !metrics[UPDATE_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] - - and: "Invocation result should contain warning of rule engine" - assert getInvocationResult(bidResponse)[0].message == "No matching rule found" - } - - def "PBS shouldn't remove bidder and emit a warning when args rule engine not fully configured in account"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with enabled rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), - createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results[0].args = null }) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - and: "Flush metrics" - flushMetrics(pbsServiceWithRulesEngineModule) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() - - and: "PBs should populate failer metrics" - def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() - assert metrics[FAILER_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] == 1 - - and: "PBs shouldn't contain noop and call metrics" - assert !metrics[CALL_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] - assert !metrics[NOOP_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] - assert !metrics[UPDATE_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] - } - - def "PBS shouldn't remove bidder and emit a warning when model group rule engine not fully configured in account"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with enabled rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), - createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups = [] }) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() - - and: "Invocation result should contain warning of rule engine" - assert getInvocationResult(bidResponse)[0].message == "Rule for account ${bidRequest.accountId} is not ready" - } - - def "PBS shouldn't log default model when rule does not fired and empty model default"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.device = new Device(geo: new Geo(country: BULGARIA)) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].modelDefault = null - } - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - } - - def "PBS shouldn't remove bidder when rule engine disabled or absent in account"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with disabled or without rules engine " - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - - and: "Analytics result shouldn't contain info about rule engine" - assert !getAnalyticResults(bidResponse) - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - where: - pbRuleEngine << [createRulesEngineWithRule(false), null] - } - - def "PBS shouldn't remove bidder when rule sets disabled in account"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with disabled rules sets" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets.first.enabled = false - } - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - } - - def "PBS shouldn't remove any bidder without cache request"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - - and: "Account with rules sets" - def pbRuleEngine = createRulesEngineWithRule() - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - - and: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - } +class RuleEngineCoreSpec extends RuleEngineBaseSpec { def "PBS should remove bidder and not update analytics when bidder matched with conditions and without analytics key"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets without analytics value" @@ -440,10 +114,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - then: "Bid response should contain two seat" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 - - and: "Response should contain seat bid" + then: "Response should contain seat bid" assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder request" @@ -458,8 +129,8 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "PBs should populate call and update metrics" def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() - assert metrics[CALL_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] == 1 - assert metrics[UPDATE_METRIC.formatted(PB_RULE_ENGINE.code, PROCESSED_AUCTION_REQUEST.metricValue, PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code)] == 1 + assert metrics[CALL_METRIC] == 1 + assert metrics[UPDATE_METRIC] == 1 and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -478,7 +149,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { amx = new Amx() } updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -508,22 +178,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should populate seatNon bid with code 203" @@ -543,7 +212,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { amx = new Amx() } updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -574,22 +242,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == bidRejectionReason - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == bidRejectionReason + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should populate seatNon bid" @@ -645,22 +312,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == ERROR_NO_BID - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == ERROR_NO_BID + it.appliedTo.impIds == bidRequest.imp.id } } @@ -668,7 +334,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -702,7 +367,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -719,7 +383,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == 3 + assert bidResponse.seatbid.seat == MULTI_BID_ADAPTERS and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -736,7 +400,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -753,7 +416,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED + assert bidResponse.seatbid.seat == [OPENX] and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -765,22 +428,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == [GENERIC, AMX].sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == [GENERIC, AMX].sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should populate seatNon bid with code 203" @@ -796,7 +458,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { it.imp.add(Imp.defaultImpression) updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -812,10 +473,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == 2 - - and: "Bid response should contain seatBids" + then: "Bid response should contain seatBids" assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() and: "PBs should perform bidder request" @@ -828,22 +486,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should populate seatNon bid with code 203" @@ -863,7 +520,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { amx = new Amx() } updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -880,7 +536,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED assert bidResponse.seatbid.seat == [GENERIC] and: "PBs should perform bidder request" @@ -893,22 +548,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved == [AMX, OPENX] - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved == [AMX, OPENX] + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should populate seatNon bid with code 203" @@ -928,7 +582,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { amx = new Amx() } updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -945,7 +598,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == 3 + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -960,22 +613,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - !impResult.values.biddersRemoved - !impResult.values.seatNonBid - impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + !it.values.biddersRemoved + !it.values.seatNonBid + it.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] } } @@ -983,7 +635,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -1000,10 +651,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - then: "Bid response should contain two seat" - assert bidResponse.seatbid.size() == 2 - - and: "Response should contain seat bid" + then: "Response should contain seat bid" assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder request" @@ -1016,22 +664,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -1046,17 +693,18 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules engine and several rule sets" + def firstResults = [createRuleEngineModelRuleWithExcludeResult(GENERIC), + createRuleEngineModelRuleWithExcludeResult(AMX), + createRuleEngineModelRuleWithExcludeResult(OPENX)] + def secondResult = [createRuleEngineModelRuleWithExcludeResult(AMX)] def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC), - createRuleEngineModelRuleWithExcludeResult(AMX), - createRuleEngineModelRuleWithExcludeResult(OPENX)] + it.ruleSets[0].modelGroups[0].rules[0].results = firstResults it.ruleSets[0].stage = stage as Stage it.ruleSets.add(RuleSet.createRuleSets()) - it.ruleSets[1].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(AMX)] + it.ruleSets[1].modelGroups[0].rules[0].results = secondResult } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -1067,10 +715,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - then: "Bid response should contain two seat" - assert bidResponse.seatbid.size() == 2 - - and: "Response should contain seat bid" + then: "Response should contain seat bid" assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX].sort() and: "PBs should perform bidder request" @@ -1083,22 +728,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[1].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -1116,7 +760,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with few model group" @@ -1135,8 +778,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -1159,7 +801,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with few model group" @@ -1168,7 +809,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { it.weight = 1 it.rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] } - it.ruleSets[0].modelGroups.add(RulesEngineModelGroups.createRulesModuleGroup()) + it.ruleSets[0].modelGroups.add(RulesEngineModelGroup.createRulesModuleGroup()) it.ruleSets[0].modelGroups[1].tap { it.weight = 100 it.rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] @@ -1184,7 +825,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() and: "PBs should perform bidder requests" @@ -1197,22 +837,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[1] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -1227,7 +866,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with default model" @@ -1247,7 +885,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -1263,30 +900,28 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } } def "PBS should log the default model group and shouldn't modify response when other rules not fire"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.device = new Device(geo: new Geo(country: BULGARIA)) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + updateBidRequestWithGeoCountry(it, BULGARIA) } and: "Account with default model" @@ -1306,8 +941,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -1322,23 +956,22 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == analyticsValue - impResult.values.resultFunction == LOG_A_TAG.value - impResult.values.conditionFired == DEFAULT_CONDITIONS - impResult.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] - - !impResult.values.biddersRemoved - !impResult.values.seatNonBid + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == analyticsValue + it.values.resultFunction == LOG_A_TAG.value + it.values.conditionFired == DEFAULT_CONDITIONS + it.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + + !it.values.biddersRemoved + !it.values.seatNonBid } } @@ -1346,7 +979,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with default model" @@ -1380,22 +1012,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -1411,9 +1042,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def startTime = Instant.now() and: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Create account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -1437,7 +1066,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { assert bidder.getBidderRequests(bidRequest.id) and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "Analytics result shouldn't contain info about rule engine" assert !getAnalyticResults(bidResponse) @@ -1463,7 +1092,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rule engine config" @@ -1485,7 +1113,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -1498,22 +1125,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -1528,7 +1154,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rule engine config" @@ -1550,8 +1175,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -1572,9 +1196,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def startTime = Instant.now() and: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -1595,8 +1217,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -1619,9 +1240,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS should exclude bidder when dataCenterIn match with condition"() { given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -1642,7 +1261,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -1655,22 +1273,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -1683,9 +1300,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS shouldn't exclude bidder when dataCentersIn not match with condition"() { given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -1706,8 +1321,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -1725,9 +1339,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS should exclude bidder when dataCenter match with condition"() { given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -1748,7 +1360,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -1761,22 +1372,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -1789,9 +1399,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS shouldn't exclude bidder when dataCenter not match with condition"() { given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -1812,8 +1420,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -1831,9 +1438,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS should exclude bidder when channel match with condition"() { given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -1854,7 +1459,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -1867,22 +1471,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -1895,9 +1498,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS shouldn't exclude bidder when channel not match with condition"() { given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -1918,8 +1519,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -1938,7 +1538,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS should exclude bidder when eidAvailable match with condition"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) user = new User(eids: [Eid.getDefaultEid()]) } @@ -1958,7 +1557,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -1971,22 +1569,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -2000,7 +1597,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS shouldn't exclude bidder when eidAvailable not match with condition"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) user = new User(eids: eids) } @@ -2023,8 +1619,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -2050,7 +1645,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rule engine config" @@ -2072,7 +1666,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { assert bidder.getBidderRequests(bidRequest.id) and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid @@ -2094,7 +1688,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def eid = Eid.getDefaultEid() def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) user = new User(eids: [eid]) } @@ -2117,7 +1710,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -2130,22 +1722,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -2159,7 +1750,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS shouldn't exclude bidder when eidIn match with condition"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) user = new User(eids: [Eid.getDefaultEid()]) } @@ -2182,8 +1772,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -2202,7 +1791,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS should exclude bidder when userFpdAvailable match with condition"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) user = requestedUfpUser } @@ -2222,7 +1810,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -2235,23 +1822,23 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } + and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] @@ -2268,7 +1855,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS shouldn't exclude bidder when userFpdAvailable not match with condition"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) user = requestedUfpUser } @@ -2288,8 +1874,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -2328,7 +1913,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -2341,22 +1925,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -2369,27 +1952,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { where: bidRequest << [ getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) user = new User(data: [Data.defaultData]) }, getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) user = new User(ext: new UserExt(data: UserExtData.FPDUserExtData)) }, getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) site.content = new Content(data: [Data.defaultData]) }, getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) site.ext = new SiteExt(data: SiteExtData.FPDSiteExtData) }, getDefaultBidRequestWithMultiplyBidders(APP).tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) app.content = new Content(data: [Data.defaultData]) }, getDefaultBidRequestWithMultiplyBidders(APP).tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) app.ext = new AppExt(data: new AppExtData(language: PBSUtils.randomString)) }, ] @@ -2412,8 +1989,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -2431,29 +2007,23 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { where: bidRequest << [ getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) user = new User(data: null, ext: new UserExt(data: null)) }, getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) user = new User(ext: new UserExt(data: null)) }, getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) site.content = new Content(data: [null]) site.ext = new SiteExt(data: null) }, getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) site.ext = new SiteExt(data: null) }, getDefaultBidRequestWithMultiplyBidders(APP).tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) app.content = new Content(data: [null]) app.ext = new AppExt(data: null) }, getDefaultBidRequestWithMultiplyBidders(APP).tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) app.ext = new AppExt(data: null) }, ] @@ -2462,7 +2032,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS should exclude bidder when gppSidAvailable match with condition"() { given: "Default bid request with multiply bidder" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) regs = new Regs(gppSid: [PBSUtils.getRandomEnum(GppSectionId).getIntValue()]) } @@ -2482,7 +2051,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -2495,22 +2063,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -2524,7 +2091,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS shouldn't exclude bidder when gppSidAvailable not match with condition"() { given: "Default bid request with multiply bidder" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) regs = new Regs(gppSid: gppSid) } @@ -2544,8 +2110,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -2570,7 +2135,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Default bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) regs = new Regs(gdpr: 0, gppSid: [PBSUtils.getRandomEnum(GppSectionId, [GppSectionId.TCF_EU_V2]).getIntValue()]) } @@ -2593,7 +2157,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { assert bidder.getBidderRequests(bidRequest.id) and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid @@ -2616,7 +2180,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Default bid request with multiply bidder" def gppSectionId = PBSUtils.getRandomEnum(GppSectionId).getIntValue() def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) regs = new Regs(gppSid: [gppSectionId]) } @@ -2639,7 +2202,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -2652,22 +2214,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved == groups.rules.first.results.first.args.bidders + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -2682,7 +2243,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Default bid request with multiply bidder" def gppSectionId = PBSUtils.getRandomEnum(GppSectionId) def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) regs = new Regs(gppSid: [gppSectionId.getIntValue()]) } @@ -2705,8 +2265,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -2725,7 +2284,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS should exclude bidder when tcfInScope match with condition"() { given: "Default bid request with multiply bidder" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) regs = new Regs(gdpr: gdpr) user = new User(ext: new UserExt(consent: new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) @@ -2752,7 +2310,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -2765,22 +2322,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved == groups.rules.first.results.first.args.bidders - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved == groups.rules.first.results.first.args.bidders + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -2792,14 +2348,13 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { where: gdpr | condition - 1 | TRUE as String - 0 | FALSE as String + 1 | 'true' + 0 | 'false' } def "PBS shouldn't exclude bidder when tcfInScope not match with condition"() { given: "Default bid request with multiply bidder" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) regs = new Regs(gdpr: gdpr) user = new User(ext: new UserExt(consent: new TcfConsent.Builder() .setPurposesLITransparency(BASIC_ADS) @@ -2826,8 +2381,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -2853,9 +2407,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def startTime = Instant.now() and: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Create account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -2879,7 +2431,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { assert bidder.getBidderRequests(bidRequest.id) and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "Analytics result should contain info about rule engine" assert !getAnalyticResults(bidResponse) @@ -2899,15 +2451,13 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS should exclude bidder when percent match with condition"() { given: "Default bid request with multiply bidder" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Create rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema[0].tap { it.function = PERCENT - it.args = new RuleEngineFunctionArgs(percent: percent as Integer) + it.args = new RuleEngineFunctionArgs(percent: PBSUtils.getRandomNumber(100)) } } @@ -2922,7 +2472,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -2935,22 +2484,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -2959,16 +2507,11 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - - where: - percent << [100, PBSUtils.getRandomNumber(100)] } def "PBS shouldn't exclude bidder when percent not match with condition"() { given: "Default bid request with multiply bidder" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Create account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -2989,8 +2532,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -3014,9 +2556,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def startTime = Instant.now() and: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Create account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -3040,7 +2580,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { assert bidder.getBidderRequests(bidRequest.id) and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBS response should not contain seatNonBid" assert !bidResponse.ext.seatnonbid @@ -3063,8 +2603,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def keyField = "key" def keyString = PBSUtils.randomString def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - ext.prebid.kvps = [(keyString): keyField] + ext.prebid.keyValuePairs = [(keyString): keyField] } and: "Create rule engine config" @@ -3087,7 +2626,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -3100,22 +2638,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -3130,8 +2667,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Default bid request with multiply bidder" def key = PBSUtils.randomString def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - ext.prebid.kvps = [(key): PBSUtils.randomString] + ext.prebid.keyValuePairs = [(key): PBSUtils.randomString] } and: "Create rule engine config" @@ -3151,8 +2687,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -3171,7 +2706,9 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS should exclude bidder when domain match with condition"() { given: "Default bid request with multiply bidder" def randomDomain = PBSUtils.randomString - def bidRequest = bidRequestClosure(randomDomain) + def bidRequest = getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { + updatePublisherDomain(it, distributionChannel, randomDomain) + } and: "Create account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -3192,7 +2729,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -3205,22 +2741,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -3231,29 +2766,16 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE where: - distributionChannel | bidRequestClosure - SITE | { String domain -> - getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { - it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - } - APP | { String domain -> - getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { - it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - } - DOOH | { String domain -> - getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { - it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - } + distributionChannel << [SITE, APP, DOOH] } def "PBS shouldn't exclude bidder when domain not match with condition"() { - given: "Create account with rule engine config" + given: "Default bid request with random domain" + def bidRequest = getDefaultBidRequestWithMultiplyBidders(distributionCahannel).tap { + updatePublisherDomain(it, distributionCahannel, PBSUtils.randomString) + } + + and: "Create account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].tap { schema = [new RuleEngineModelSchema(function: DOMAIN)] @@ -3272,8 +2794,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -3289,20 +2810,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { assert !getAnalyticResults(bidResponse) where: - bidRequest << [ - getDefaultBidRequestWithMultiplyBidders().tap { - it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - }, - getDefaultBidRequestWithMultiplyBidders(APP).tap { - it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - }, - getDefaultBidRequestWithMultiplyBidders(DOOH).tap { - it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - }] - + distributionCahannel << [SITE, APP, DOOH] } def "PBS should reject processing the rule engine when the domainIn schema function contains incompatible arguments"() { @@ -3335,7 +2843,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { assert bidder.getBidderRequests(bidRequest.id) and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid @@ -3353,43 +2861,38 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { "Field 'domains' is required and has to be an array of strings") where: - distributionChannel | bidRequestClosure - SITE | { String domain -> - getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { - it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - } - SITE | { String domain -> - getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { - it.site.domain = domain - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - } - APP | { String domain -> - getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { - it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - } - APP | { String domain -> - getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { - it.app.domain = domain - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - } - DOOH | { String domain -> - getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { - it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - } - DOOH | { String domain -> - getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { - it.dooh.domain = domain - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - } + bidRequestClosure << [ + { String domain -> + getDefaultBidRequestWithMultiplyBidders(SITE).tap { + it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + } + }, + { String domain -> + getDefaultBidRequestWithMultiplyBidders(SITE).tap { + it.site.domain = domain + } + }, + { String domain -> + getDefaultBidRequestWithMultiplyBidders(APP).tap { + it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + } + }, + { String domain -> + getDefaultBidRequestWithMultiplyBidders(APP).tap { + it.app.domain = domain + } + }, + { String domain -> + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + } + }, + { String domain -> + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + it.dooh.domain = domain + } + } + ] } def "PBS should exclude bidder when domainIn match with condition"() { @@ -3416,7 +2919,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -3429,22 +2931,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -3455,43 +2956,38 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE where: - distributionChannel | bidRequestClosure - SITE | { String domain -> - getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { - it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - } - SITE | { String domain -> - getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { - it.site.domain = domain - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - } - APP | { String domain -> - getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { - it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - } - APP | { String domain -> - getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { - it.app.domain = domain - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - } - DOOH | { String domain -> - getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { - it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - } - DOOH | { String domain -> - getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { - it.dooh.domain = domain - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } - } + bidRequestClosure << [ + { String domain -> + getDefaultBidRequestWithMultiplyBidders(SITE).tap { + it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) + } + }, + { String domain -> + getDefaultBidRequestWithMultiplyBidders(SITE).tap { + it.site.domain = domain + } + }, + { String domain -> + getDefaultBidRequestWithMultiplyBidders(APP).tap { + it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) + } + }, + { String domain -> + getDefaultBidRequestWithMultiplyBidders(APP).tap { + it.app.domain = domain + } + }, + { String domain -> + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) + } + }, + { String domain -> + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + it.dooh.domain = domain + } + } + ] } def "PBS shouldn't exclude bidder when domainIn not match with condition"() { @@ -3514,8 +3010,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -3532,28 +3027,22 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { where: bidRequest << [ - getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + getDefaultBidRequestWithMultiplyBidders(SITE).tap { it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) }, - getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) + getDefaultBidRequestWithMultiplyBidders(SITE).tap { it.site.domain = PBSUtils.randomString }, getDefaultBidRequestWithMultiplyBidders(APP).tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) }, getDefaultBidRequestWithMultiplyBidders(APP).tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) it.app.domain = PBSUtils.randomString }, getDefaultBidRequestWithMultiplyBidders(DOOH).tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) }, getDefaultBidRequestWithMultiplyBidders(DOOH).tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) it.dooh.domain = PBSUtils.randomString }] @@ -3563,7 +3052,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Default bid request with multiply bidder" def bundle = PBSUtils.randomString def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) app.bundle = bundle } @@ -3586,7 +3074,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -3599,22 +3086,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -3628,7 +3114,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS shouldn't exclude bidder when bundle not match with condition"() { given: "Default bid request with multiply bidder" def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) app.bundle = PBSUtils.randomString } @@ -3651,8 +3136,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -3674,7 +3158,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Default bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) app.bundle = PBSUtils.randomString } @@ -3697,7 +3180,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { assert bidder.getBidderRequests(bidRequest.id) and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "Analytics result shouldn't contain info about rule engine" assert !getAnalyticResults(bidResponse) @@ -3712,14 +3195,14 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { then: "Logs should contain error" def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) assert getLogsByText(logs, "Failed to parse rule-engine config for account ${bidRequest.accountId}: " + - "Function '${BUNDLE_IN}' configuration is invalid: Field 'bundles' is required and has to be an array of strings").size() == 1 + "Function '${BUNDLE_IN}' configuration is invalid: " + + "Field 'bundles' is required and has to be an array of strings").size() == 1 } def "PBS should exclude bidder when bundleIn match with condition"() { given: "Default bid request with multiply bidders" def bundle = PBSUtils.randomString def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) app.bundle = bundle } @@ -3742,7 +3225,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -3755,22 +3237,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -3784,7 +3265,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS shouldn't exclude bidder when bundleIn not match with condition"() { given: "Default bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) app.bundle = PBSUtils.randomString } @@ -3807,8 +3287,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -3829,9 +3308,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def startTime = Instant.now() and: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Create account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -3852,7 +3329,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { assert bidder.getBidderRequests(bidRequest.id) and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid @@ -3867,7 +3344,8 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { then: "Logs should contain error" def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) assert getLogsByText(logs, "Failed to parse rule-engine config for account ${bidRequest.accountId}: " + - "Function '${MEDIA_TYPE_IN}' configuration is invalid: Field 'types' is required and has to be an array of strings").size() == 1 + "Function '${MEDIA_TYPE_IN}' configuration is invalid:" + + " Field 'types' is required and has to be an array of strings").size() == 1 where: mediaTypeInArgs << [null, PBSUtils.randomNumber] @@ -3875,9 +3353,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS should exclude bidder when mediaTypeIn match with condition"() { given: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Setup bidder response" def bidderResponse = BidResponse.getDefaultBidResponse(bidRequest) @@ -3902,7 +3378,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -3915,22 +3390,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -3943,9 +3417,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS shouldn't exclude bidder when mediaTypeIn not match with condition"() { given: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Create account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -3966,8 +3438,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -4011,7 +3482,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -4024,22 +3494,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -4053,25 +3522,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { bidRequestClosure << [ { tagId -> getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) imp[0].tagId = tagId } }, { gpid -> getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) imp[0].ext.gpid = gpid } }, { pbAdSlot -> getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) imp[0].ext.data = new ImpExtContextData(pbAdSlot: pbAdSlot) } }, { storedRequestId -> getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: storedRequestId) } } @@ -4080,9 +3545,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS shouldn't exclude bidder when adUnitCode not match with condition"() { given: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Create account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -4103,8 +3566,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -4126,7 +3588,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Default bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) imp[0].tagId = PBSUtils.randomString imp[0].ext.gpid = PBSUtils.randomString imp[0].ext.data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) @@ -4158,7 +3619,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { assert bidder.getBidderRequests(bidRequest.id) and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid @@ -4207,7 +3668,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -4220,22 +3680,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -4249,7 +3708,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { bidRequestClosure << [ { storedRequestId -> getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) imp[0].tagId = PBSUtils.randomString imp[0].ext.gpid = PBSUtils.randomString imp[0].ext.data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) @@ -4258,8 +3716,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { }, { pbAdSlot -> getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - imp[0].tagId = PBSUtils.randomString imp[0].ext.gpid = PBSUtils.randomString imp[0].ext.data = new ImpExtContextData(pbAdSlot: pbAdSlot) imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomString) @@ -4267,7 +3723,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { }, { gpid -> getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) imp[0].tagId = PBSUtils.randomString imp[0].ext.gpid = gpid imp[0].ext.data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) @@ -4276,7 +3731,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { }, { tagId -> getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) imp[0].tagId = tagId imp[0].ext.gpid = PBSUtils.randomString imp[0].ext.data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) @@ -4288,9 +3742,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS shouldn't exclude bidder when adUnitCodeIn not match with condition"() { given: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) - } + def bidRequest = getDefaultBidRequestWithMultiplyBidders() and: "Create account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -4308,8 +3760,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -4329,7 +3780,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Default bid request with multiply bidders" def deviceType = PBSUtils.getRandomEnum(DeviceType) def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) device = new Device(devicetype: deviceType) } @@ -4352,7 +3802,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -4365,22 +3814,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -4395,7 +3843,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Default bid request with multiply bidders" def requestDeviceType = PBSUtils.getRandomEnum(DeviceType) def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) device = new Device(devicetype: requestDeviceType) } @@ -4418,8 +3865,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -4441,7 +3887,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Default bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) device = new Device(devicetype: PBSUtils.getRandomEnum(DeviceType)) } @@ -4467,7 +3912,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { assert bidder.getBidderRequests(bidRequest.id) and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBS response shouldn't contain seatNonBid" assert !bidResponse.ext.seatnonbid @@ -4489,7 +3934,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { given: "Default bid request with multiply bidders" def deviceType = PBSUtils.getRandomEnum(DeviceType) def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) device = new Device(devicetype: deviceType) } @@ -4512,7 +3956,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() and: "PBs should perform bidder requests" @@ -4525,22 +3968,21 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == ModuleName.PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -4554,7 +3996,6 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def "PBS shouldn't exclude bidder when deviceTypeIn not match with condition"() { given: "Default bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) device = new Device(devicetype: PBSUtils.getRandomEnum(DeviceType)) } @@ -4577,8 +4018,7 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -4593,4 +4033,22 @@ class RuleEngineGeneralSpec extends RuleEngineBaseSpec { and: "Analytics result shouldn't contain info about module exclude" assert !getAnalyticResults(bidResponse) } + + private static BidRequest updatePublisherDomain(BidRequest bidRequest, DistributionChannel distributionChannel, String domain) { + if (distributionChannel == SITE) { + bidRequest.tap { + it.site.publisher.domain = domain + } + } + if (distributionChannel == APP) { + bidRequest.tap { + it.app.publisher.domain = domain + } + } + if (distributionChannel == DOOH) { + bidRequest.tap { + it.dooh.publisher.domain = domain + } + } + } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy index 5347136fec3..cedf69cd3e4 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy @@ -3,6 +3,7 @@ package org.prebid.server.functional.tests.module.pbruleengine import org.prebid.server.functional.model.UidsCookie import org.prebid.server.functional.util.HttpUtil +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE import static org.prebid.server.functional.model.bidder.BidderName.AMX import static org.prebid.server.functional.model.bidder.BidderName.GENERIC import static org.prebid.server.functional.model.bidder.BidderName.OPENX @@ -18,7 +19,6 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -40,8 +40,7 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == [OPENX, AMX] and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -61,7 +60,6 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -83,11 +81,7 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - 1 - assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings @@ -96,19 +90,22 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about module exclude" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS - def impResult = result.results[0] + + and: "Analytics result detail info" def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - assert impResult.status == SUCCESS - assert impResult.values.analyticsKey == groups.analyticsKey - assert impResult.values.modelVersion == groups.version - assert impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - assert impResult.values.resultFunction == groups.rules.first.results.first.function.value - assert impResult.values.conditionFired == groups.rules.first.conditions.first - assert impResult.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - assert impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - assert impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 @@ -122,7 +119,6 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -140,8 +136,7 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) @@ -161,7 +156,6 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -195,22 +189,21 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == [AMX, OPENX, GENERIC].sort() - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == [AMX, OPENX, GENERIC].sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -225,7 +218,6 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { given: "Bid request with multiply imps bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -247,7 +239,6 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) then: "Bid response should contain seat" - assert bidResponse.seatbid.size() == ONE_BIDDER_REQUESTED assert bidResponse.seatbid.seat == [GENERIC] and: "PBs should perform bidder request" @@ -260,22 +251,21 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE_MODULE_NAME_CODE + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" - def impResult = result.results[0] def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll { - impResult.status == SUCCESS - impResult.values.analyticsKey == groups.analyticsKey - impResult.values.modelVersion == groups.version - impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - impResult.values.resultFunction == groups.rules.first.results.first.function.value - impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == [OPENX, AMX] - impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - impResult.appliedTo.impIds == bidRequest.imp.id + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == [OPENX, AMX] + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id } and: "Response should seatNon bid with code 203" @@ -290,7 +280,6 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) - updateBidRequestWithTraceVerboseAndReturnAllBidStatus(it) } and: "Account with rules sets" @@ -305,8 +294,7 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.size() == BIDDERS_REQUESTED - assert bidResponse.seatbid.seat.sort() == [GENERIC, OPENX, AMX].sort() + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder requests" assert bidder.getBidderRequests(bidRequest.id) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy new file mode 100644 index 00000000000..2c7441b7a89 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy @@ -0,0 +1,323 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.pricefloors.Country.BULGARIA + +class RuleEngineValidationSpec extends RuleEngineBaseSpec { + + def "PBS shouldn't remove bidder when rule engine not fully configured in account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with enabled rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), pbRulesEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + and: "Flush metrics" + flushMetrics(pbsServiceWithRulesEngineModule) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "PBs should populate call and noop metrics" + def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() + assert metrics[CALL_METRIC] == 1 + assert metrics[NOOP_METRIC] == 1 + + and: "PBs should populate update metrics" + assert !metrics[UPDATE_METRIC] + + where: + pbRulesEngine << [ + createRulesEngineWithRule().tap { it.ruleSets = [] }, + createRulesEngineWithRule().tap { it.ruleSets[0].stage = null }, + createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].schema = [] }, + createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules = [] }, + createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results = [] } + ] + } + + def "PBS shouldn't remove bidder when rule engine not fully configured in account wihout rule conditions"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with enabled rules engine" + def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].conditions = [] } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + and: "Flush metrics" + flushMetrics(pbsServiceWithRulesEngineModule) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "PBs should populate failer metrics" + def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() + assert metrics[FAILER_METRIC] == 1 + + and: "PBs shouldn't contain noop and call metrics" + assert !metrics[CALL_METRIC] + assert !metrics[NOOP_METRIC] + assert !metrics[UPDATE_METRIC] + + and: "Invocation result should contain warning of rule engine" + assert getInvocationResult(bidResponse)[0].message == "No matching rule found" + } + + def "PBS shouldn't remove bidder and emit a warning when args rule engine not fully configured in account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with enabled rules engine" + def pbRuleEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups[0].rules[0].results[0].args = null } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + and: "Flush metrics" + flushMetrics(pbsServiceWithRulesEngineModule) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should populate failer metrics" + def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() + assert metrics[FAILER_METRIC] == 1 + + and: "PBs shouldn't contain noop and call metrics" + assert !metrics[CALL_METRIC] + assert !metrics[NOOP_METRIC] + assert !metrics[UPDATE_METRIC] + } + + def "PBS shouldn't remove bidder and emit a warning when model group rule engine not fully configured in account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with enabled rules engine" + def pbRulesEngine = createRulesEngineWithRule().tap { it.ruleSets[0].modelGroups = [] } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.getAccountId(), pbRulesEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "Invocation result should contain warning of rule engine" + assert getInvocationResult(bidResponse)[0].message == "Rule for account ${bidRequest.accountId} is not ready" + } + + def "PBS shouldn't log default model when rule does not fired and empty model default"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it, BULGARIA) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].modelDefault = null + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS shouldn't remove bidder when rule engine disabled or absent in account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + where: + pbRuleEngine << [createRulesEngineWithRule(false), null] + } + + def "PBS shouldn't remove bidder when rule sets disabled in account"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with disabled rules sets" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets.first.enabled = false + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + } + + def "PBS shouldn't remove any bidder without cache account request"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rules sets" + def pbRuleEngine = createRulesEngineWithRule() + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + and: "Invocation result should contain warning of rule engine" + assert getInvocationResult(bidResponse)[0].message == "Rule for account ${bidRequest.accountId} is not ready" + } + +} From 3d5be3ba8d0194eb20fa3ace846a6e5f47533f32 Mon Sep 17 00:00:00 2001 From: markiian Date: Tue, 30 Sep 2025 14:56:44 +0300 Subject: [PATCH 14/18] Minor update --- .../tests/module/ModuleBaseSpec.groovy | 6 ++-- .../pbruleengine/RuleEngineCoreSpec.groovy | 32 +++++++++++-------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy index b4cca8b9189..50ca4add3f2 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy @@ -62,9 +62,9 @@ class ModuleBaseSpec extends BaseSpec { protected static Map getRulesEngineSettings(Endpoint endpoint = OPENRTB2_AUCTION, Stage stage = PROCESSED_AUCTION_REQUEST) { ["hooks.${PB_RULE_ENGINE.code}.enabled" : "true", "hooks.${PB_RULE_ENGINE.code}.rule-cache.expire-after-minutes" : "10000", - "hooks.${PB_RULE_ENGINE.code}.rule-cache.max-size" : "200", - "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-initial-delay-millis": "1", - "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-max-delay-millis" : "1", + "hooks.${PB_RULE_ENGINE.code}.rule-cache.max-size" : "20000", + "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-initial-delay-millis": "10000", + "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-max-delay-millis" : "10000", "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-exponential-factor" : "1.2", "hooks.${PB_RULE_ENGINE.code}.rule-parsing.retry-exponential-jitter" : "1.2", "hooks.host-execution-plan" : encode(ExecutionPlan.getSingleEndpointExecutionPlan(endpoint, PB_RULE_ENGINE, [stage]))] diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy index db95fa959f5..9603eddc470 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy @@ -383,7 +383,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) then: "Bid response should contain seats" - assert bidResponse.seatbid.seat == MULTI_BID_ADAPTERS + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -446,11 +446,13 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { } and: "Response should populate seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert bidResponse.ext.seatnonbid.size() == 2 + def seatNonBid = bidResponse.ext.seatnonbid + assert seatNonBid.seat.sort() == [GENERIC, AMX].sort() + assert seatNonBid.nonBid.impId.flatten() == [bidRequest.imp[0].id, bidRequest.imp[0].id] + assert seatNonBid.nonBid.statusCode.flatten() == + [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE, + REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] } def "PBS should remove bidder by device geo from imps when bidder excluded in account config"() { @@ -684,7 +686,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC + assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } @@ -748,7 +750,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC + assert seatNonBid.seat == AMX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE @@ -894,9 +896,6 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] @@ -916,6 +915,13 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE it.appliedTo.impIds == bidRequest.imp.id } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } def "PBS should log the default model group and shouldn't modify response when other rules not fire"() { @@ -1145,7 +1151,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC + assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } @@ -1293,7 +1299,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Response should seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC + assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } From e3f35b16b74ee2a57e11c7654a50ade8a42e4ebb Mon Sep 17 00:00:00 2001 From: markiian Date: Thu, 2 Oct 2025 14:48:00 +0300 Subject: [PATCH 15/18] Update after minor change logic --- .../pbruleengine/RuleEngineAliasSpec.groovy | 33 +++--- .../pbruleengine/RuleEngineBaseSpec.groovy | 9 +- .../pbruleengine/RuleEngineCoreSpec.groovy | 86 +++------------ .../pbruleengine/RuleEngineSyncSpec.groovy | 100 +++++++++--------- .../RuleEngineValidationSpec.groovy | 43 ++++---- 5 files changed, 105 insertions(+), 166 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy index be9c112cfdc..311deeb49ea 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy @@ -78,11 +78,12 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { } and: "Response should populate seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == [OPENX, AMX, GENERIC].sort() - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert bidResponse.ext.seatnonbid.size() == 3 + def seatNonBid = bidResponse.ext.seatnonbid + assert seatNonBid.seat.sort() == [OPENX, AMX, GENERIC].sort() + assert seatNonBid.nonBid.impId.flatten().unique().sort() == bidRequest.imp.id.sort() + assert seatNonBid.nonBid.statusCode.unique().flatten() == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE, + REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] } def "PBS should remove hard alias bidder from imps when hard alias bidder excluded in account config"() { @@ -139,11 +140,11 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { impResult.appliedTo.impIds == [bidRequest.imp[1].id] } - and: "Response should seatNon bid with code 203" + and: "Response should populate seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == OPENX_ALIAS - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].impId == bidRequest.imp[1].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } @@ -202,12 +203,14 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { impResult.appliedTo.impIds == bidRequest.imp.id } - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == ALIAS - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + and: "Response should populate seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 3 + def seatNonBid = bidResponse.ext.seatnonbid + assert seatNonBid.seat.sort() == [OPENX, GENERIC, AMX].sort() + assert seatNonBid.nonBid.impId.flatten().unique().sort() == bidRequest.imp.id.sort() + assert seatNonBid.nonBid.statusCode.unique().flatten() == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE, + REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE, + REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] } def "PBS should remove soft alias bidder from imps when soft alias bidder excluded in account config"() { @@ -265,11 +268,11 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { impResult.appliedTo.impIds == [bidRequest.imp[1].id] } - and: "Response should seatNon bid with code 203" + and: "Response should populate seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == ALIAS - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].impId == bidRequest.imp[1].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy index d4b52d38803..d074d1dbd1a 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy @@ -29,6 +29,7 @@ import static org.prebid.server.functional.model.pricefloors.Country.USA import static org.prebid.server.functional.model.request.auction.DistributionChannel.SITE import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE import static org.prebid.server.functional.testcontainers.Dependencies.getNetworkServiceContainer +import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID abstract class RuleEngineBaseSpec extends ModuleBaseSpec { @@ -36,7 +37,6 @@ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { protected static final String APPLIED_FOR_ALL_IMPS = "*" protected static final String DEFAULT_CONDITIONS = "default" protected final static String CALL_METRIC = "modules.module.${PB_RULE_ENGINE.code}.stage.${PROCESSED_AUCTION_REQUEST.metricValue}.hook.${PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code}.call" - protected final static String FAILER_METRIC = "modules.module.${PB_RULE_ENGINE.code}.stage.${PROCESSED_AUCTION_REQUEST.metricValue}.hook.${PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code}.failure" protected final static String NOOP_METRIC = "modules.module.${PB_RULE_ENGINE.code}.stage.${PROCESSED_AUCTION_REQUEST.metricValue}.hook.${PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code}.success.noop" protected final static String UPDATE_METRIC = "modules.module.${PB_RULE_ENGINE.code}.stage.${PROCESSED_AUCTION_REQUEST.metricValue}.hook.${PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code}.success.update" protected static final Map OPENX_CONFIG = ["adapters.${OPENX}.enabled" : "true", @@ -46,7 +46,12 @@ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { protected static final Map OPENX_ALIAS_CONFIG = ["adapters.${OPENX}.aliases.${OPENX_ALIAS}.enabled" : "true", "adapters.${OPENX}.aliases.${OPENX_ALIAS}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] protected static final String CONFIG_DATA_CENTER = PBSUtils.randomString - protected static final PrebidServerService pbsServiceWithRulesEngineModule = pbsServiceFactory.getService( + private static final String USER_SYNC_URL = "$networkServiceContainer.rootUri/generic-usersync" + private static final Map GENERIC_CONFIG = [ + "adapters.${GENERIC.value}.usersync.redirect.url" : USER_SYNC_URL, + "adapters.${GENERIC.value}.usersync.redirect.support-cors": false as String, + "adapters.${GENERIC.value}.meta-info.vendor-id" : GENERIC_VENDOR_ID as String] + protected static final PrebidServerService pbsServiceWithRulesEngineModule = pbsServiceFactory.getService(GENERIC_CONFIG + getRulesEngineSettings() + AMX_CONFIG + OPENX_CONFIG + OPENX_ALIAS_CONFIG + ['datacenter-region': CONFIG_DATA_CENTER]) protected static BidRequest getDefaultBidRequestWithMultiplyBidders(DistributionChannel distributionChannel = SITE) { diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy index 9603eddc470..49d9474f088 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy @@ -199,72 +199,9 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == GENERIC - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS should remove bidder from imps and update seatNonBid with other code when seatNonBid override and exclude bidder in account config"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(Imp.defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - } - updateBidRequestWithGeoCountry(it) - } - - and: "Account with rules sets" - def bidRejectionReason = PBSUtils.getRandomEnum(BidRejectionReason) - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args.seatNonBid = bidRejectionReason - } - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [OPENX, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == bidRejectionReason - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should populate seatNon bid" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == bidRejectionReason + assert seatNonBid.nonBid.impId.sort() == bidRequest.imp.id.sort() + assert seatNonBid.nonBid.statusCode == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE, + REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] } def "PBS should remove bidder from imps and not update seatNonBid when returnAllBidStatus disabled and exclude bidder in account config"() { @@ -452,7 +389,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert seatNonBid.nonBid.impId.flatten() == [bidRequest.imp[0].id, bidRequest.imp[0].id] assert seatNonBid.nonBid.statusCode.flatten() == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE, - REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] + REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] } def "PBS should remove bidder by device geo from imps when bidder excluded in account config"() { @@ -509,8 +446,9 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert bidResponse.ext.seatnonbid.size() == 1 def seatNonBid = bidResponse.ext.seatnonbid[0] assert seatNonBid.seat == GENERIC - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert seatNonBid.nonBid.impId.sort() == bidRequest.imp.id.sort() + assert seatNonBid.nonBid.statusCode == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE, + REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] } def "PBS should leave only include bidder at imps when bidder include in account config"() { @@ -568,11 +506,11 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { } and: "Response should populate seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + assert bidResponse.ext.seatnonbid.size() == 2 + def seatNonBid = bidResponse.ext.seatnonbid + assert seatNonBid.seat.sort() == [AMX, OPENX].sort() + assert seatNonBid.nonBid.impId.flatten().unique().sort() == bidRequest.imp.id.sort() + assert seatNonBid.nonBid.statusCode.flatten().unique() == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] } def "PBS should only logATag when present only function log a tag"() { diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy index cedf69cd3e4..135c92c1d1d 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy @@ -15,7 +15,7 @@ import static org.prebid.server.functional.model.response.auction.BidRejectionRe class RuleEngineSyncSpec extends RuleEngineBaseSpec { - def "PBS shouldn't remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { + def "PBS should remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) @@ -42,21 +42,39 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { then: "Bid response should contain seats" assert bidResponse.seatbid.seat.sort() == [OPENX, AMX] - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid + and: "Analytics result should contain info about module exclude" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS - and: "Analytics result shouldn't contain info about rule engine" - assert !getAnalyticResults(bidResponse) + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should contain seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == GENERIC + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } - def "PBS should remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=false in account config"() { + def "PBS shouldn't remove bidder from imps when bidder has ID in the uids cookie and bidder excluded and ifSyncedId=false in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) @@ -83,36 +101,18 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { then: "Bid response should contain seats" assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + and: "PBS should not contain errors, warnings" assert !bidResponse.ext?.warnings assert !bidResponse.ext?.errors - and: "Analytics result should contain info about module exclude" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == GENERIC - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) } def "PBS shouldn't remove bidder from imps when bidder hasn't ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { @@ -177,7 +177,7 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) then: "Bid response shouldn't contain seat" - assert !bidResponse.seatbid.seat + assert bidResponse.seatbid.seat == [GENERIC] and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -201,20 +201,20 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue it.values.resultFunction == groups.rules.first.results.first.function.value it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == [AMX, OPENX, GENERIC].sort() + it.values.biddersRemoved.sort() == [AMX, OPENX].sort() it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE it.appliedTo.impIds == bidRequest.imp.id } - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == [OPENX, GENERIC] - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + and: "Response should contain seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 2 + def seatNonBid = bidResponse.ext.seatnonbid + assert seatNonBid.seat.sort() == [OPENX, AMX].sort() + assert seatNonBid.nonBid.impId.flatten().unique().sort() == bidRequest.imp.id.sort() + assert seatNonBid.nonBid.statusCode.flatten().unique() == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] } - def "PBS should leave only include bidder at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=false in account config"() { + def "PBS shouldn't include bidder at imps when bidder has ID in the uids cookie and bidder include and ifSyncedId=false in account config"() { given: "Bid request with multiply imps bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) @@ -238,8 +238,8 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { when: "PBS processes auction request" def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest, cookieHeader) - then: "Bid response should contain seat" - assert bidResponse.seatbid.seat == [GENERIC] + then: "Bid response shouldn't contain seat" + assert !bidResponse.seatbid.seat and: "PBs should perform bidder request" assert bidder.getBidderRequests(bidRequest.id) @@ -263,17 +263,13 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue it.values.resultFunction == groups.rules.first.results.first.function.value it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == [OPENX, AMX] + it.values.biddersRemoved.sort() == [OPENX, AMX, GENERIC].sort() it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE it.appliedTo.impIds == bidRequest.imp.id } - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == [OPENX, GENERIC] - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + and: "Response shouldn't contain seatNon bid with code 203" + assert !bidResponse.ext.seatnonbid } def "PBS should leave request bidder at imps when bidder hasn't ID in the uids cookie and bidder excluded and ifSyncedId=true in account config"() { diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy index 2c7441b7a89..177c4155abc 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy @@ -1,5 +1,7 @@ package org.prebid.server.functional.tests.module.pbruleengine +import java.time.Instant + import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule import static org.prebid.server.functional.model.pricefloors.Country.BULGARIA @@ -58,7 +60,7 @@ class RuleEngineValidationSpec extends RuleEngineBaseSpec { ] } - def "PBS shouldn't remove bidder when rule engine not fully configured in account wihout rule conditions"() { + def "PBS shouldn't remove bidder when rule engine not fully configured in account without rule conditions"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) @@ -94,17 +96,9 @@ class RuleEngineValidationSpec extends RuleEngineBaseSpec { and: "Analytics result shouldn't contain info about rule engine" assert !getAnalyticResults(bidResponse) - and: "PBs should populate failer metrics" + and: "PBs should populate noop metrics" def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() - assert metrics[FAILER_METRIC] == 1 - - and: "PBs shouldn't contain noop and call metrics" - assert !metrics[CALL_METRIC] - assert !metrics[NOOP_METRIC] - assert !metrics[UPDATE_METRIC] - - and: "Invocation result should contain warning of rule engine" - assert getInvocationResult(bidResponse)[0].message == "No matching rule found" + assert metrics[NOOP_METRIC] == 1 } def "PBS shouldn't remove bidder and emit a warning when args rule engine not fully configured in account"() { @@ -142,16 +136,14 @@ class RuleEngineValidationSpec extends RuleEngineBaseSpec { and: "PBs should populate failer metrics" def metrics = pbsServiceWithRulesEngineModule.sendCollectedMetricsRequest() - assert metrics[FAILER_METRIC] == 1 - - and: "PBs shouldn't contain noop and call metrics" - assert !metrics[CALL_METRIC] - assert !metrics[NOOP_METRIC] - assert !metrics[UPDATE_METRIC] + assert metrics[NOOP_METRIC] == 1 } def "PBS shouldn't remove bidder and emit a warning when model group rule engine not fully configured in account"() { - given: "Bid request with multiply bidders" + given: "Test start time" + def startTime = Instant.now() + + and: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) } @@ -180,8 +172,9 @@ class RuleEngineValidationSpec extends RuleEngineBaseSpec { then: "Bid response should contain seats" assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - and: "Invocation result should contain warning of rule engine" - assert getInvocationResult(bidResponse)[0].message == "Rule for account ${bidRequest.accountId} is not ready" + and: "PBs should emit failed logs" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Failed to parse rule-engine config for account $bidRequest.accountId: Weighted list cannot be empty").size() == 1 } def "PBS shouldn't log default model when rule does not fired and empty model default"() { @@ -287,7 +280,10 @@ class RuleEngineValidationSpec extends RuleEngineBaseSpec { } def "PBS shouldn't remove any bidder without cache account request"() { - given: "Bid request with multiply bidders" + given: "Test start time" + def startTime = Instant.now() + + and: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { updateBidRequestWithGeoCountry(it) } @@ -316,8 +312,9 @@ class RuleEngineValidationSpec extends RuleEngineBaseSpec { and: "Analytics result shouldn't contain info about module exclude" assert !getAnalyticResults(bidResponse) - and: "Invocation result should contain warning of rule engine" - assert getInvocationResult(bidResponse)[0].message == "Rule for account ${bidRequest.accountId} is not ready" + and: "PBs should emit failed logs" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Parsing rule for account $bidRequest.accountId").size() == 1 } } From 569fb3b3d4dda1b3f33dcc45a9377fd87ce8e5a4 Mon Sep 17 00:00:00 2001 From: markiian Date: Thu, 2 Oct 2025 17:42:07 +0300 Subject: [PATCH 16/18] GppSectionId remove TCF_EU_V2 from random array --- .../module/pbruleengine/RuleEngineCoreSpec.groovy | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy index 49d9474f088..e802421af53 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy @@ -2122,9 +2122,8 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { def "PBS should exclude bidder when gppSidIn match with condition"() { given: "Default bid request with multiply bidder" - def gppSectionId = PBSUtils.getRandomEnum(GppSectionId).getIntValue() def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - regs = new Regs(gppSid: [gppSectionId]) + regs = new Regs(gppSid: [gppSectionId.getIntValue()]) } and: "Create rule engine config" @@ -2181,11 +2180,13 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert seatNonBid.seat == OPENX assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + gppSectionId << GppSectionId.values() - GppSectionId.TCF_EU_V2 } def "PBS shouldn't exclude bidder when gppSidIn not match with condition"() { given: "Default bid request with multiply bidder" - def gppSectionId = PBSUtils.getRandomEnum(GppSectionId) def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { regs = new Regs(gppSid: [gppSectionId.getIntValue()]) } @@ -2223,6 +2224,9 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Analytics result shouldn't contain info about module exclude" assert !getAnalyticResults(bidResponse) + + where: + gppSectionId << GppSectionId.values() - GppSectionId.TCF_EU_V2 } def "PBS should exclude bidder when tcfInScope match with condition"() { From 6dc82e4a5e57c33de2b655d4d75226b6c9e78376 Mon Sep 17 00:00:00 2001 From: markiian Date: Fri, 3 Oct 2025 14:56:21 +0300 Subject: [PATCH 17/18] Major update after review --- .../model/config/PbRulesEngine.groovy | 3 + .../model/config/RuleEngineFunction.groovy | 50 +- .../config/RuleEngineModelRuleResult.groovy | 10 +- .../RuleEngineModelRuleResultsArgs.groovy | 5 +- .../model/config/RulesEngineModelGroup.groovy | 2 +- .../model/request/auction/ImpUnitCode.groovy | 10 + .../tests/module/ModuleBaseSpec.groovy | 6 + .../pbruleengine/RuleEngineAliasSpec.groovy | 53 +- .../pbruleengine/RuleEngineBaseSpec.groovy | 113 + .../pbruleengine/RuleEngineContextSpec.groovy | 1151 ++++++ .../pbruleengine/RuleEngineCoreSpec.groovy | 3146 +---------------- .../pbruleengine/RuleEngineDeviceSpec.groovy | 434 +++ .../RuleEngineInfrastructureSpec.groovy | 264 ++ .../pbruleengine/RuleEnginePrivacySpec.groovy | 909 +++++ .../pbruleengine/RuleEngineSpecialSpec.groovy | 319 ++ .../pbruleengine/RuleEngineSyncSpec.groovy | 30 +- .../RuleEngineValidationSpec.groovy | 116 +- 17 files changed, 3399 insertions(+), 3222 deletions(-) create mode 100644 src/test/groovy/org/prebid/server/functional/model/request/auction/ImpUnitCode.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineContextSpec.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineDeviceSpec.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineInfrastructureSpec.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpecialSpec.groovy diff --git a/src/test/groovy/org/prebid/server/functional/model/config/PbRulesEngine.groovy b/src/test/groovy/org/prebid/server/functional/model/config/PbRulesEngine.groovy index 279f46a0fc3..321c548394b 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/PbRulesEngine.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/PbRulesEngine.groovy @@ -1,9 +1,12 @@ package org.prebid.server.functional.model.config +import java.time.ZonedDateTime + class PbRulesEngine { Boolean enabled Boolean generateRulesFromBidderConfig + ZonedDateTime timestamp List ruleSets static PbRulesEngine createRulesEngineWithRule(Boolean enabled = true) { diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy index 2bd58fa3be2..d7e5845bc07 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy @@ -4,34 +4,36 @@ import com.fasterxml.jackson.annotation.JsonValue enum RuleEngineFunction { - DEVICE_COUNTRY("deviceCountry"), - DEVICE_COUNTRY_IN("deviceCountryIn"), - DATA_CENTER("dataCenter"), - DATA_CENTER_IN("dataCenterIn"), - CHANNEL("channel"), - EID_AVAILABLE("eidAvailable"), - EID_IN("eidIn"), - USER_FPD_AVAILABLE("userFpdAvailable"), - FPD_AVAILABLE("fpdAvailable"), - GPP_SID_AVAILABLE("gppSidAvailable"), - GPP_SID_IN("gppSidIn"), - TCF_IN_SCOPE("tcfInScope"), - PERCENT("percent"), - PREBID_KEY("prebidKey"), - DOMAIN("domain"), - DOMAIN_IN("domainIn"), - BUNDLE("bundle"), - BUNDLE_IN("bundleIn"), - MEDIA_TYPE_IN("mediaTypeIn"), - AD_UNIT_CODE("adUnitCode"), - AD_UNIT_CODE_IN("adUnitCodeIn"), - DEVICE_TYPE("deviceType"), - DEVICE_TYPE_IN("deviceTypeIn"), + DEVICE_COUNTRY("deviceCountry", null), + DEVICE_COUNTRY_IN("deviceCountryIn", "countries"), + DATA_CENTER("dataCenter", null), + DATA_CENTER_IN("dataCenterIn", "datacenters"), + CHANNEL("channel", null), + EID_AVAILABLE("eidAvailable", null), + EID_IN("eidIn", "sources"), + USER_FPD_AVAILABLE("userFpdAvailable", null), + FPD_AVAILABLE("fpdAvailable", null), + GPP_SID_AVAILABLE("gppSidAvailable", null), + GPP_SID_IN("gppSidIn", "sids"), + TCF_IN_SCOPE("tcfInScope", null), + PERCENT("percent", "pct"), + PREBID_KEY("prebidKey", "key"), + DOMAIN("domain", null), + DOMAIN_IN("domainIn", "domains"), + BUNDLE("bundle", null), + BUNDLE_IN("bundleIn", "bundles"), + MEDIA_TYPE_IN("mediaTypeIn", "types"), + AD_UNIT_CODE("adUnitCode",null), + AD_UNIT_CODE_IN("adUnitCodeIn","codes"), + DEVICE_TYPE("deviceType",null), + DEVICE_TYPE_IN("deviceTypeIn","types"), private String value + private String fieldName - RuleEngineFunction(String value) { + RuleEngineFunction(String value, String fieldName) { this.value = value + this.fieldName = fieldName } @JsonValue diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResult.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResult.groovy index e548a93b874..cb898171623 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResult.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResult.groovy @@ -13,17 +13,19 @@ class RuleEngineModelRuleResult { ResultFunction function RuleEngineModelRuleResultsArgs args - static RuleEngineModelRuleResult createRuleEngineModelRuleWithIncludeResult(BidderName bidderName = ACEEX) { + static RuleEngineModelRuleResult createRuleEngineModelRuleWithIncludeResult(BidderName bidderName = ACEEX, + Boolean ifSyncedId = false) { new RuleEngineModelRuleResult().tap { it.function = INCLUDE_BIDDERS - it.args = RuleEngineModelRuleResultsArgs.createRuleEngineModelRuleResultsArgs(bidderName) + it.args = RuleEngineModelRuleResultsArgs.createRuleEngineModelRuleResultsArgs(bidderName, ifSyncedId) } } - static RuleEngineModelRuleResult createRuleEngineModelRuleWithExcludeResult(BidderName bidderName = OPENX) { + static RuleEngineModelRuleResult createRuleEngineModelRuleWithExcludeResult(BidderName bidderName = OPENX, + Boolean ifSyncedId = false) { new RuleEngineModelRuleResult().tap { it.function = EXCLUDE_BIDDER - it.args = RuleEngineModelRuleResultsArgs.createRuleEngineModelRuleResultsArgs(bidderName) + it.args = RuleEngineModelRuleResultsArgs.createRuleEngineModelRuleResultsArgs(bidderName, ifSyncedId) } } diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy index 24e781a467e..c1ec70eb853 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineModelRuleResultsArgs.groovy @@ -15,14 +15,15 @@ class RuleEngineModelRuleResultsArgs { @JsonProperty("ifSyncedId") Boolean ifSyncedId - static RuleEngineModelRuleResultsArgs createRuleEngineModelRuleResultsArgs(BidderName bidderName){ + static RuleEngineModelRuleResultsArgs createRuleEngineModelRuleResultsArgs(BidderName bidderName, Boolean ifSyncedId) { new RuleEngineModelRuleResultsArgs().tap { it.bidders = [bidderName] it.analyticsValue = PBSUtils.randomString + it.ifSyncedId = ifSyncedId } } - static RuleEngineModelRuleResultsArgs createRuleEngineModelRuleResultsArgsOnlyATag(){ + static RuleEngineModelRuleResultsArgs createRuleEngineModelRuleResultsArgsOnlyATag() { new RuleEngineModelRuleResultsArgs().tap { it.analyticsValue = PBSUtils.randomString } diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroup.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroup.groovy index b4021e87f21..f9361465cd4 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroup.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RulesEngineModelGroup.groovy @@ -18,7 +18,7 @@ class RulesEngineModelGroup { static RulesEngineModelGroup createRulesModuleGroup() { new RulesEngineModelGroup().tap { - it.weight = PBSUtils.getRandomNumber(0, 100) + it.weight = PBSUtils.getRandomNumber(1, 100) it.version = PBSUtils.randomString it.analyticsKey = PBSUtils.randomString it.schema = [createDeviceCountryInSchema()] diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpUnitCode.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpUnitCode.groovy new file mode 100644 index 00000000000..15f737d317e --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpUnitCode.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.request.auction + +enum ImpUnitCode { + + TAG_ID, + GPID, + PB_AD_SLOT, + STORED_REQUEST +} + diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy index 50ca4add3f2..c1ecc250c78 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy @@ -11,6 +11,7 @@ import org.prebid.server.functional.tests.BaseSpec import static org.prebid.server.functional.model.ModuleName.ORTB2_BLOCKING import static org.prebid.server.functional.model.ModuleName.PB_RESPONSE_CORRECTION import static org.prebid.server.functional.model.ModuleName.PB_RICHMEDIA_FILTER +import static org.prebid.server.functional.model.ModuleName.PB_REQUEST_CORRECTION import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE import static org.prebid.server.functional.model.config.Endpoint.OPENRTB2_AUCTION import static org.prebid.server.functional.model.config.Stage.ALL_PROCESSED_BID_RESPONSES @@ -59,6 +60,11 @@ class ModuleBaseSpec extends BaseSpec { ["hooks.${ORTB2_BLOCKING.code}.enabled": isEnabled as String] } + protected static Map getRequestCorrectionSettings(Endpoint endpoint = OPENRTB2_AUCTION, Stage stage = PROCESSED_AUCTION_REQUEST) { + ["hooks.${PB_REQUEST_CORRECTION.code}.enabled": "true", + "hooks.host-execution-plan" : encode(ExecutionPlan.getSingleEndpointExecutionPlan(endpoint, PB_REQUEST_CORRECTION, [stage]))] + } + protected static Map getRulesEngineSettings(Endpoint endpoint = OPENRTB2_AUCTION, Stage stage = PROCESSED_AUCTION_REQUEST) { ["hooks.${PB_RULE_ENGINE.code}.enabled" : "true", "hooks.${PB_RULE_ENGINE.code}.rule-cache.expire-after-minutes" : "10000", diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy index 311deeb49ea..af43209a87f 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy @@ -1,12 +1,8 @@ package org.prebid.server.functional.tests.module.pbruleengine -import org.prebid.server.functional.model.ModuleName -import org.prebid.server.functional.model.bidder.Generic -import org.prebid.server.functional.model.bidder.Openx -import org.prebid.server.functional.model.request.auction.Amx -import org.prebid.server.functional.model.request.auction.Bidder import org.prebid.server.functional.model.request.auction.Imp +import static org.prebid.server.functional.model.ModuleName.* import static org.prebid.server.functional.model.bidder.BidderName.ALIAS import static org.prebid.server.functional.model.bidder.BidderName.AMX import static org.prebid.server.functional.model.bidder.BidderName.GENERIC @@ -22,10 +18,11 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { def "PBS should leave only hard alias bidder at imps when hard alias bidder include in account config"() { given: "Bid request with multiply imps bidders" + def bidders = [OPENX, AMX, OPENX_ALIAS, GENERIC] def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { it.imp.add(Imp.defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - updateImpWithOpenXAndAmxAndOpenXAliasBidder(it) + it.imp[1].tap { + updateBidderImp(it, bidders) } updateBidRequestWithGeoCountry(it) } @@ -59,7 +56,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" @@ -72,7 +69,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue impResult.values.resultFunction == groups.rules.first.results.first.function.value impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == [GENERIC, OPENX, AMX] + impResult.values.biddersRemoved.sort() == [OPENX, AMX, GENERIC].sort() impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE impResult.appliedTo.impIds == bidRequest.imp.id } @@ -82,16 +79,15 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { def seatNonBid = bidResponse.ext.seatnonbid assert seatNonBid.seat.sort() == [OPENX, AMX, GENERIC].sort() assert seatNonBid.nonBid.impId.flatten().unique().sort() == bidRequest.imp.id.sort() - assert seatNonBid.nonBid.statusCode.unique().flatten() == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE, - REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] + assert seatNonBid.nonBid.statusCode.flatten().unique() == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] } def "PBS should remove hard alias bidder from imps when hard alias bidder excluded in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { it.imp.add(Imp.defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - updateImpWithOpenXAndAmxAndOpenXAliasBidder(it) + it.imp[1].tap { + updateBidderImp(it, [OPENX, OPENX_ALIAS, AMX]) } updateBidRequestWithGeoCountry(it) } @@ -122,7 +118,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" @@ -152,8 +148,8 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { given: "Bid request with multiply imps bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { it.imp.add(Imp.defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - getAliasAndAmxAndOpenXAndWithoutGenericBidder(it) + it.imp[1].tap { + updateBidderImp(it, [ALIAS, AMX, OPENX]) } ext.prebid.aliases = [(ALIAS.value): GENERIC] updateBidRequestWithGeoCountry(it) @@ -185,7 +181,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" @@ -217,8 +213,8 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { it.imp.add(Imp.defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - getAliasAndAmxAndOpenXAndWithoutGenericBidder(it) + it.imp[1].tap { + updateBidderImp(it, [ALIAS, AMX, OPENX]) } ext.prebid.aliases = [(ALIAS.value): GENERIC] updateBidRequestWithGeoCountry(it) @@ -250,7 +246,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" @@ -275,21 +271,4 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { assert seatNonBid.nonBid[0].impId == bidRequest.imp[1].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } - - private static void updateImpWithOpenXAndAmxAndOpenXAliasBidder(Bidder bidder) { - bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - openxAlias = Openx.defaultOpenx - } - } - - private static void getAliasAndAmxAndOpenXAndWithoutGenericBidder(Bidder bidder) { - bidder.tap { - alias = new Generic() - generic = null - amx = new Amx() - openx = Openx.defaultOpenx - } - } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy index d074d1dbd1a..b180e9a01af 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy @@ -14,11 +14,14 @@ import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.request.auction.Device import org.prebid.server.functional.model.request.auction.DistributionChannel import org.prebid.server.functional.model.request.auction.Geo +import org.prebid.server.functional.model.request.auction.Imp +import org.prebid.server.functional.model.request.auction.ImpUnitCode import org.prebid.server.functional.service.PrebidServerService import org.prebid.server.functional.tests.module.ModuleBaseSpec import org.prebid.server.functional.util.PBSUtils import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE +import static org.prebid.server.functional.model.bidder.BidderName.ALIAS import static org.prebid.server.functional.model.bidder.BidderName.AMX import static org.prebid.server.functional.model.bidder.BidderName.GENERIC import static org.prebid.server.functional.model.bidder.BidderName.OPENX @@ -26,7 +29,13 @@ import static org.prebid.server.functional.model.bidder.BidderName.OPENX_ALIAS import static org.prebid.server.functional.model.config.ModuleHookImplementation.PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST import static org.prebid.server.functional.model.config.Stage.PROCESSED_AUCTION_REQUEST import static org.prebid.server.functional.model.pricefloors.Country.USA +import static org.prebid.server.functional.model.request.auction.DistributionChannel.APP +import static org.prebid.server.functional.model.request.auction.DistributionChannel.DOOH import static org.prebid.server.functional.model.request.auction.DistributionChannel.SITE +import static org.prebid.server.functional.model.request.auction.ImpUnitCode.GPID +import static org.prebid.server.functional.model.request.auction.ImpUnitCode.PB_AD_SLOT +import static org.prebid.server.functional.model.request.auction.ImpUnitCode.STORED_REQUEST +import static org.prebid.server.functional.model.request.auction.ImpUnitCode.TAG_ID import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE import static org.prebid.server.functional.testcontainers.Dependencies.getNetworkServiceContainer import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID @@ -39,6 +48,30 @@ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { protected final static String CALL_METRIC = "modules.module.${PB_RULE_ENGINE.code}.stage.${PROCESSED_AUCTION_REQUEST.metricValue}.hook.${PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code}.call" protected final static String NOOP_METRIC = "modules.module.${PB_RULE_ENGINE.code}.stage.${PROCESSED_AUCTION_REQUEST.metricValue}.hook.${PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code}.success.noop" protected final static String UPDATE_METRIC = "modules.module.${PB_RULE_ENGINE.code}.stage.${PROCESSED_AUCTION_REQUEST.metricValue}.hook.${PB_RULES_ENGINE_PROCESSED_AUCTION_REQUEST.code}.success.update" + protected final static Closure INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING = { accountId, functionType -> + "Failed to parse rule-engine config for account $accountId: " + + "Function '$functionType' configuration is invalid: " + + "Field '$functionType.fieldName' is required and has to be an array of strings" + } + + protected final static Closure INVALID_CONFIGURATION_FOR_SINGLE_STRING_LOG_WARNING = { accountId, functionType -> + "Failed to parse rule-engine config for account $accountId: " + + "Function '$functionType' configuration is invalid: " + + "Field '$functionType.fieldName' is required and has to be a string" + } + + protected final static Closure INVALID_CONFIGURATION_FOR_SINGLE_INTEGER_LOG_WARNING = { accountId, functionType -> + "Failed to parse rule-engine config for account $accountId: " + + "Function '$functionType' configuration is invalid: " + + "Field '$functionType.fieldName' is required and has to be an integer" + } + + protected final static Closure INVALID_CONFIGURATION_FOR_INTEGERS_LOG_WARNING = { accountId, functionType -> + "Failed to parse rule-engine config for account $accountId: " + + "Function '$functionType' configuration is invalid: " + + "Field '$functionType.fieldName' is required and has to be an array of integers" + } + protected static final Map OPENX_CONFIG = ["adapters.${OPENX}.enabled" : "true", "adapters.${OPENX}.endpoint": "$networkServiceContainer.rootUri/auction".toString()] protected static final Map AMX_CONFIG = ["adapters.${AMX}.enabled" : "true", @@ -64,6 +97,16 @@ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { } } + protected static void updateBidderImp(Imp imp, List bidders = MULTI_BID_ADAPTERS) { + imp.ext.prebid.bidder.tap { + openx = bidders.contains(OPENX) ? Openx.defaultOpenx : null + openxAlias = bidders.contains(OPENX_ALIAS) ? Openx.defaultOpenx : null + amx = bidders.contains(AMX) ? new Amx() : null + generic = bidders.contains(GENERIC) ? new Generic() : null + alias = bidders.contains(ALIAS) ? new Generic() : null + } + } + protected static void updateBidRequestWithGeoCountry(BidRequest bidRequest, Country country = USA) { bidRequest.device = new Device(geo: new Geo(country: country)) } @@ -72,4 +115,74 @@ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { def accountHooksConfiguration = new AccountHooksConfiguration(modules: new PbsModulesConfig(pbRuleEngine: ruleEngine)) new Account(uuid: accountId, config: new AccountConfig(hooks: accountHooksConfiguration)) } + + protected static BidRequest createBidRequestWithDomains(DistributionChannel type, String domain, boolean usePublisher = true) { + def request = getDefaultBidRequestWithMultiplyBidders(type) + + switch (type) { + case SITE: + if (usePublisher) request.site.publisher.domain = domain + else request.site.domain = domain + break + case APP: + if (usePublisher) request.app.publisher.domain = domain + else request.app.domain = domain + break + case DOOH: + if (usePublisher) request.dooh.publisher.domain = domain + else request.dooh.domain = domain + break + } + request + } + + protected static BidRequest updatePublisherDomain(BidRequest bidRequest, DistributionChannel distributionChannel, String domain) { + if (distributionChannel == SITE) { + bidRequest.tap { + it.site.publisher.domain = domain + } + } + if (distributionChannel == APP) { + bidRequest.tap { + it.app.publisher.domain = domain + } + } + if (distributionChannel == DOOH) { + bidRequest.tap { + it.dooh.publisher.domain = domain + } + } + } + + protected static String getImpAdUnitCodeByCode(Imp imp, ImpUnitCode impUnitCode) { + if (TAG_ID == impUnitCode) { + return imp.tagId + } + if (GPID == impUnitCode) { + return imp.ext.gpid + } + if (PB_AD_SLOT == impUnitCode) { + return imp.ext.data.pbAdSlot + } + if (STORED_REQUEST == impUnitCode) { + return imp.ext.prebid.storedRequest.id + } + return null + } + + protected static String getImpAdUnitCode(Imp imp) { + if (imp.ext.gpid) { + return imp.ext.gpid + } + if (imp.tagId) { + return imp.tagId + } + if (imp.ext.data.pbAdSlot) { + return imp.ext.data.pbAdSlot + } + if (imp.ext.prebid.storedRequest.id) { + return imp.ext.prebid.storedRequest.id + } + return null + } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineContextSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineContextSpec.groovy new file mode 100644 index 00000000000..c957deffcb9 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineContextSpec.groovy @@ -0,0 +1,1151 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.ChannelType +import org.prebid.server.functional.model.config.RuleEngineFunctionArgs +import org.prebid.server.functional.model.config.RuleEngineModelSchema +import org.prebid.server.functional.model.db.StoredImp +import org.prebid.server.functional.model.pricefloors.MediaType +import org.prebid.server.functional.model.request.auction.DistributionChannel +import org.prebid.server.functional.model.request.auction.Imp +import org.prebid.server.functional.model.request.auction.ImpExtContextData +import org.prebid.server.functional.model.request.auction.PrebidStoredRequest +import org.prebid.server.functional.model.request.auction.Publisher +import org.prebid.server.functional.model.response.auction.BidResponse +import org.prebid.server.functional.util.PBSUtils + +import java.time.Instant + +import static org.prebid.server.functional.model.ChannelType.WEB +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.RuleEngineFunction.AD_UNIT_CODE +import static org.prebid.server.functional.model.config.RuleEngineFunction.AD_UNIT_CODE_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.BUNDLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.BUNDLE_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.CHANNEL +import static org.prebid.server.functional.model.config.RuleEngineFunction.DOMAIN +import static org.prebid.server.functional.model.config.RuleEngineFunction.DOMAIN_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.MEDIA_TYPE_IN +import static org.prebid.server.functional.model.pricefloors.MediaType.BANNER +import static org.prebid.server.functional.model.request.auction.DistributionChannel.APP +import static org.prebid.server.functional.model.request.auction.DistributionChannel.DOOH +import static org.prebid.server.functional.model.request.auction.DistributionChannel.SITE +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.request.auction.ImpUnitCode.GPID +import static org.prebid.server.functional.model.request.auction.ImpUnitCode.PB_AD_SLOT +import static org.prebid.server.functional.model.request.auction.ImpUnitCode.STORED_REQUEST +import static org.prebid.server.functional.model.request.auction.ImpUnitCode.TAG_ID +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + +class RuleEngineContextSpec extends RuleEngineBaseSpec { + + def "PBS should exclude bidder when channel match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: CHANNEL)] + rules[0].conditions = [WEB.value] + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when channel not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: CHANNEL)] + rules[0].conditions = [PBSUtils.getRandomEnum(ChannelType, [WEB]).value] + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should exclude bidder when domain match with condition"() { + given: "Default bid request with multiply bidder" + def randomDomain = PBSUtils.randomString + def bidRequest = getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { + updatePublisherDomain(it, distributionChannel, randomDomain) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DOMAIN)] + rules[0].conditions = [randomDomain] + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + distributionChannel << DistributionChannel.values() + } + + def "PBS shouldn't exclude bidder when domain not match with condition"() { + given: "Default bid request with random domain" + def bidRequest = getDefaultBidRequestWithMultiplyBidders(distributionCahannel).tap { + updatePublisherDomain(it, distributionCahannel, PBSUtils.randomString) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DOMAIN)] + rules[0].conditions = [PBSUtils.randomString] + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + distributionCahannel << DistributionChannel.values() + } + + def "PBS should reject processing the rule engine when the domainIn schema function contains incompatible arguments"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiplyB bidders" + def randomDomain = PBSUtils.randomString + def bidRequest = bidRequestClosure(randomDomain) + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DOMAIN_IN + it.args = new RuleEngineFunctionArgs(domains: [PBSUtils.randomNumber]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING(bidRequest.accountId, DOMAIN_IN)) + + where: + bidRequestClosure << [ + { String domain -> + getDefaultBidRequestWithMultiplyBidders(SITE).tap { + it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + } + }, + { String domain -> + getDefaultBidRequestWithMultiplyBidders(SITE).tap { + it.site.domain = domain + } + }, + { String domain -> + getDefaultBidRequestWithMultiplyBidders(APP).tap { + it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + } + }, + { String domain -> + getDefaultBidRequestWithMultiplyBidders(APP).tap { + it.app.domain = domain + } + }, + { String domain -> + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + } + }, + { String domain -> + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + it.dooh.domain = domain + } + } + ] + } + + def "PBS should exclude bidder when domainIn match with condition"() { + given: "Default bid request with multiply bidder" + def randomDomain = PBSUtils.randomString + def bidRequest = createBidRequestWithDomains(type, randomDomain, usePublisher) + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DOMAIN_IN + it.args = new RuleEngineFunctionArgs(domains: [PBSUtils.randomString, randomDomain]) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + type | usePublisher + SITE | true + SITE | false + APP | true + APP | false + DOOH | true + DOOH | false + } + + def "PBS shouldn't exclude bidder when domainIn not match with condition"() { + given: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DOMAIN_IN + it.args = new RuleEngineFunctionArgs(domains: [PBSUtils.randomString, PBSUtils.randomString]) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + bidRequest << [ + getDefaultBidRequestWithMultiplyBidders(SITE).tap { + it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + }, + getDefaultBidRequestWithMultiplyBidders(SITE).tap { + it.site.domain = PBSUtils.randomString + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + it.app.domain = PBSUtils.randomString + }, + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) + }, + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + it.dooh.domain = PBSUtils.randomString + }] + + } + + def "PBS should exclude bidder when bundle match with condition"() { + given: "Default bid request with multiply bidder" + def bundle = PBSUtils.randomString + def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.bundle = bundle + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: BUNDLE)] + rules[0].conditions = [bundle] + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when bundle not match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.bundle = PBSUtils.randomString + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: BUNDLE)] + rules[0].conditions = [PBSUtils.randomString] + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should reject processing the rule engine when the bundleIn schema function contains incompatible arguments"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.bundle = PBSUtils.randomString + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = BUNDLE_IN + it.args = new RuleEngineFunctionArgs(bundles: [PBSUtils.randomNumber]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + then: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING(bidRequest.accountId, BUNDLE_IN)) + } + + def "PBS should exclude bidder when bundleIn match with condition"() { + given: "Default bid request with multiply bidders" + def bundle = PBSUtils.randomString + def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.bundle = bundle + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = BUNDLE_IN + it.args = new RuleEngineFunctionArgs(bundles: [bundle]) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Account cache" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + + def "PBS shouldn't exclude bidder when bundleIn not match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.bundle = PBSUtils.randomString + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = BUNDLE_IN + it.args = new RuleEngineFunctionArgs(bundles: [PBSUtils.randomString, PBSUtils.randomString]) + } + } + + and: "Save account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Account cache" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should reject processing the rule engine when the mediaTypeIn schema function contains incompatible arguments"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = MEDIA_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: [mediaTypeInArgs]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + then: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING(bidRequest.accountId, MEDIA_TYPE_IN)) + + where: + mediaTypeInArgs << [null, PBSUtils.randomNumber] + } + + def "PBS should exclude bidder when mediaTypeIn match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Setup bidder response" + def bidderResponse = BidResponse.getDefaultBidResponse(bidRequest) + bidder.setResponse(bidRequest.id, bidderResponse) + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = MEDIA_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: [BANNER.value]) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when mediaTypeIn not match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = MEDIA_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: [PBSUtils.getRandomEnum(MediaType, [BANNER])]) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should exclude bidder when adUnitCode match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + imp[0].ext.gpid = gpid + imp[0].tagId = tagId + imp[0].ext.data = new ImpExtContextData(pbAdSlot: pbAdSlot) + imp[0].ext.prebid.storedRequest = prebidStoredRequest + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: AD_UNIT_CODE)] + rules[0].conditions = [getImpAdUnitCode(bidRequest.imp[0])] + } + } + + and: "Save storedImp into DB" + def storedImp = StoredImp.getStoredImp(bidRequest) + storedImpDao.save(storedImp) + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + gpid | tagId | pbAdSlot | prebidStoredRequest + PBSUtils.getRandomString() | null | null | null + null | PBSUtils.getRandomString() | null | null + null | null | PBSUtils.getRandomString() | null + null | null | null | new PrebidStoredRequest(id: PBSUtils.getRandomString()) + PBSUtils.getRandomString() | PBSUtils.getRandomString() | PBSUtils.getRandomString() | new PrebidStoredRequest(id: PBSUtils.getRandomString()) + null | null | null | new PrebidStoredRequest(id: PBSUtils.getRandomString()) + null | null | PBSUtils.getRandomString() | null + null | PBSUtils.getRandomString() | null | null + PBSUtils.getRandomString() | null | null | null + } + + def "PBS shouldn't exclude bidder when adUnitCode not match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: AD_UNIT_CODE)] + rules[0].conditions = [PBSUtils.randomString] + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should reject processing the rule engine when the adUnitCodeIn schema function contains incompatible arguments"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + imp[0].tagId = PBSUtils.randomString + imp[0].ext.gpid = PBSUtils.randomString + imp[0].ext.data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) + imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomString) + } + + and: "Save storedImp into DB" + def storedImp = StoredImp.getStoredImp(bidRequest).tap { + impData = Imp.getDefaultImpression() + } + storedImpDao.save(storedImp) + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = AD_UNIT_CODE_IN + it.args = new RuleEngineFunctionArgs(codes: [arguments]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING(bidRequest.accountId, AD_UNIT_CODE_IN)) + + where: + arguments << [PBSUtils.randomBoolean, PBSUtils.randomNumber] + } + + def "PBS should exclude bidder when adUnitCodeIn match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + imp[0].tagId = PBSUtils.randomString + imp[0].ext.gpid = PBSUtils.randomString + imp[0].ext.data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) + imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomString) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = AD_UNIT_CODE_IN + it.args = new RuleEngineFunctionArgs(codes: [PBSUtils.randomString, + getImpAdUnitCodeByCode(bidRequest.imp[0], impUnitCode)]) + } + } + + and: "Save storedImp into DB" + def storedImp = StoredImp.getStoredImp(bidRequest) + storedImpDao.save(storedImp) + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + impUnitCode << [TAG_ID, GPID, PB_AD_SLOT, STORED_REQUEST] + } + + def "PBS shouldn't exclude bidder when adUnitCodeIn not match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = AD_UNIT_CODE_IN + it.args = new RuleEngineFunctionArgs(codes: [PBSUtils.randomString, PBSUtils.randomString]) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy index e802421af53..68c718695de 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy @@ -1,94 +1,31 @@ package org.prebid.server.functional.tests.module.pbruleengine -import org.prebid.server.functional.model.ChannelType -import org.prebid.server.functional.model.ModuleName import org.prebid.server.functional.model.bidder.Openx -import org.prebid.server.functional.model.config.RuleEngineFunctionArgs import org.prebid.server.functional.model.config.RuleEngineModelDefault import org.prebid.server.functional.model.config.RuleEngineModelDefaultArgs -import org.prebid.server.functional.model.config.RuleEngineModelSchema import org.prebid.server.functional.model.config.RuleSet import org.prebid.server.functional.model.config.RulesEngineModelGroup import org.prebid.server.functional.model.config.Stage -import org.prebid.server.functional.model.db.StoredImp -import org.prebid.server.functional.model.pricefloors.Country -import org.prebid.server.functional.model.pricefloors.MediaType -import org.prebid.server.functional.model.request.GppSectionId import org.prebid.server.functional.model.request.auction.Amx -import org.prebid.server.functional.model.request.auction.AppExt -import org.prebid.server.functional.model.request.auction.AppExtData -import org.prebid.server.functional.model.request.auction.BidRequest -import org.prebid.server.functional.model.request.auction.Content -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.DeviceType -import org.prebid.server.functional.model.request.auction.DistributionChannel -import org.prebid.server.functional.model.request.auction.Eid import org.prebid.server.functional.model.request.auction.Imp -import org.prebid.server.functional.model.request.auction.ImpExtContextData -import org.prebid.server.functional.model.request.auction.PrebidStoredRequest -import org.prebid.server.functional.model.request.auction.Publisher -import org.prebid.server.functional.model.request.auction.Regs -import org.prebid.server.functional.model.request.auction.SiteExt -import org.prebid.server.functional.model.request.auction.SiteExtData -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.model.response.auction.BidRejectionReason -import org.prebid.server.functional.model.response.auction.BidResponse import org.prebid.server.functional.util.PBSUtils -import org.prebid.server.functional.util.privacy.TcfConsent -import java.time.Instant - -import static java.lang.Boolean.FALSE -import static java.lang.Boolean.TRUE -import static org.prebid.server.functional.model.ChannelType.WEB +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE import static org.prebid.server.functional.model.bidder.BidderName.AMX import static org.prebid.server.functional.model.bidder.BidderName.GENERIC import static org.prebid.server.functional.model.bidder.BidderName.OPENX import static org.prebid.server.functional.model.bidder.BidderName.UNKNOWN import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule import static org.prebid.server.functional.model.config.ResultFunction.LOG_A_TAG -import static org.prebid.server.functional.model.config.RuleEngineFunction.AD_UNIT_CODE -import static org.prebid.server.functional.model.config.RuleEngineFunction.AD_UNIT_CODE_IN -import static org.prebid.server.functional.model.config.RuleEngineFunction.BUNDLE -import static org.prebid.server.functional.model.config.RuleEngineFunction.BUNDLE_IN -import static org.prebid.server.functional.model.config.RuleEngineFunction.CHANNEL -import static org.prebid.server.functional.model.config.RuleEngineFunction.DATA_CENTER -import static org.prebid.server.functional.model.config.RuleEngineFunction.DATA_CENTER_IN -import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY -import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY_IN -import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_TYPE -import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_TYPE_IN -import static org.prebid.server.functional.model.config.RuleEngineFunction.DOMAIN -import static org.prebid.server.functional.model.config.RuleEngineFunction.DOMAIN_IN -import static org.prebid.server.functional.model.config.RuleEngineFunction.EID_AVAILABLE -import static org.prebid.server.functional.model.config.RuleEngineFunction.EID_IN -import static org.prebid.server.functional.model.config.RuleEngineFunction.FPD_AVAILABLE -import static org.prebid.server.functional.model.config.RuleEngineFunction.GPP_SID_AVAILABLE -import static org.prebid.server.functional.model.config.RuleEngineFunction.GPP_SID_IN -import static org.prebid.server.functional.model.config.RuleEngineFunction.MEDIA_TYPE_IN -import static org.prebid.server.functional.model.config.RuleEngineFunction.PERCENT -import static org.prebid.server.functional.model.config.RuleEngineFunction.PREBID_KEY -import static org.prebid.server.functional.model.config.RuleEngineFunction.TCF_IN_SCOPE -import static org.prebid.server.functional.model.config.RuleEngineFunction.USER_FPD_AVAILABLE import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithExcludeResult import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithIncludeResult import static org.prebid.server.functional.model.config.RuleEngineModelRuleResult.createRuleEngineModelRuleWithLogATagResult import static org.prebid.server.functional.model.config.Stage.PROCESSED_AUCTION_REQUEST import static org.prebid.server.functional.model.pricefloors.Country.BULGARIA -import static org.prebid.server.functional.model.pricefloors.Country.USA -import static org.prebid.server.functional.model.pricefloors.MediaType.BANNER -import static org.prebid.server.functional.model.request.auction.DistributionChannel.APP -import static org.prebid.server.functional.model.request.auction.DistributionChannel.DOOH -import static org.prebid.server.functional.model.request.auction.DistributionChannel.SITE import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS import static org.prebid.server.functional.model.request.auction.TraceLevel.VERBOSE import static org.prebid.server.functional.model.response.auction.BidRejectionReason.ERROR_NO_BID import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE -import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID -import static org.prebid.server.functional.util.privacy.TcfConsent.PurposeId.BASIC_ADS class RuleEngineCoreSpec extends RuleEngineBaseSpec { @@ -178,7 +115,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" @@ -249,7 +186,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" @@ -365,7 +302,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" @@ -425,7 +362,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" @@ -488,7 +425,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" @@ -553,7 +490,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" @@ -604,7 +541,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" @@ -668,7 +605,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" @@ -696,46 +633,6 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { stage << Stage.values() - PROCESSED_AUCTION_REQUEST } - def "PBS shouldn't take rule with higher weight and remove bidder when weight negative or zero"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithGeoCountry(it) - } - - and: "Account with few model group" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - it.weight = weight - } - } - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - - where: - weight << [PBSUtils.randomNegativeNumber, 0] - } def "PBS should take rule with higher weight and remove bidder when two model group with different weight"() { given: "Bid request with multiply bidders" @@ -777,7 +674,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" @@ -837,7 +734,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" @@ -900,7 +797,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" @@ -956,268 +853,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { and: "Analytics result should contain info about name and status" def analyticsResult = getAnalyticResults(bidResponse) def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS should reject processing rule engine when #function schema function contain args"() { - given: "Test start time" - def startTime = Instant.now() - - and: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = function - it.args = RuleEngineFunctionArgs.defaultFunctionArgs - } - } - - and: "Save account with rule engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "Analytics result shouldn't contain info about rule engine" - assert !getAnalyticResults(bidResponse) - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Logs should contain error" - def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) - assert getLogsByText(logs, "Failed to parse rule-engine config for account ${bidRequest.accountId}: " + - "Function '${function.value}' configuration is invalid: No arguments allowed") - - where: - function << [DEVICE_TYPE, AD_UNIT_CODE, BUNDLE, DOMAIN, TCF_IN_SCOPE, GPP_SID_AVAILABLE, FPD_AVAILABLE, - USER_FPD_AVAILABLE, EID_AVAILABLE, CHANNEL, DEVICE_COUNTRY] - } - - def "PBS should exclude bidder when deviceCountry match with condition"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithGeoCountry(it) - } - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema = [new RuleEngineModelSchema(function: DEVICE_COUNTRY)] - rules[0].conditions = [USA.toString()] - } - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS shouldn't exclude bidder when deviceCountry not match with condition"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithGeoCountry(it) - } - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema[0].function = DEVICE_COUNTRY - rules[0].conditions = [PBSUtils.getRandomEnum(Country, [USA]).toString()] - } - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Account cache" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - } - - def "PBS should reject processing rule engine when dataCenterIn schema function args contain invalid data"() { - given: "Test start time" - def startTime = Instant.now() - - and: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = DATA_CENTER_IN - it.args = new RuleEngineFunctionArgs(countries: [CONFIG_DATA_CENTER]) - } - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - - and: "Logs should contain error" - def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) - assert getLogsByText(logs, "Function '${DATA_CENTER_IN}' configuration is invalid: " + - "Field 'datacenters' is required and has to be an array of strings") - } - - def "PBS should exclude bidder when dataCenterIn match with condition"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = DATA_CENTER_IN - it.args = new RuleEngineFunctionArgs(datacenters: [CONFIG_DATA_CENTER]) - } - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code + assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS and: "Analytics result detail info" @@ -1242,2761 +878,5 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } - def "PBS shouldn't exclude bidder when dataCentersIn not match with condition"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = DATA_CENTER_IN - it.args = new RuleEngineFunctionArgs(datacenters: [PBSUtils.randomString]) - } - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - } - - def "PBS should exclude bidder when dataCenter match with condition"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema = [new RuleEngineModelSchema(function: DATA_CENTER)] - rules[0].conditions = [CONFIG_DATA_CENTER] - } - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS shouldn't exclude bidder when dataCenter not match with condition"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema = [new RuleEngineModelSchema(function: DATA_CENTER)] - rules[0].conditions = [PBSUtils.randomString] - } - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - } - - def "PBS should exclude bidder when channel match with condition"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema = [new RuleEngineModelSchema(function: CHANNEL)] - rules[0].conditions = [WEB.value] - } - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS shouldn't exclude bidder when channel not match with condition"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema = [new RuleEngineModelSchema(function: CHANNEL)] - rules[0].conditions = [PBSUtils.getRandomEnum(ChannelType, [WEB]).value] - } - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - } - - def "PBS should exclude bidder when eidAvailable match with condition"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - user = new User(eids: [Eid.getDefaultEid()]) - } - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: EID_AVAILABLE)] - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS shouldn't exclude bidder when eidAvailable not match with condition"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - user = new User(eids: eids) - } - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema[0].function = EID_AVAILABLE - rules[0].conditions = [TRUE as String] - } - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - - where: - eids << [null, []] - } - - def "PBS should reject processing rule engine when eidIn schema function args contain invalid data"() { - given: "Test start time" - def startTime = Instant.now() - - and: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - updateBidRequestWithGeoCountry(it) - } - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = EID_IN - it.args = new RuleEngineFunctionArgs(sources: [PBSUtils.randomNumber]) - } - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - - and: "Logs should contain error" - def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) - assert getLogsByText(logs, "Function '${EID_IN}' configuration is invalid: " + - "Field 'sources' is required and has to be an array of strings") - } - - def "PBS should exclude bidder when eidIn match with condition"() { - given: "Bid request with multiply bidders" - def eid = Eid.getDefaultEid() - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - user = new User(eids: [eid]) - } - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = EID_IN - it.args = new RuleEngineFunctionArgs(sources: [PBSUtils.randomString, eid.source, PBSUtils.randomString]) - } - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS shouldn't exclude bidder when eidIn match with condition"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - user = new User(eids: [Eid.getDefaultEid()]) - } - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = EID_IN - it.args = new RuleEngineFunctionArgs(sources: [PBSUtils.randomString, PBSUtils.randomString]) - } - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - } - - def "PBS should exclude bidder when userFpdAvailable match with condition"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - user = requestedUfpUser - } - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: USER_FPD_AVAILABLE)] - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - - where: - requestedUfpUser << [new User(data: [Data.defaultData], ext: new UserExt(data: UserExtData.FPDUserExtData)), - new User(ext: new UserExt(data: UserExtData.FPDUserExtData)), - new User(data: [Data.defaultData])] - } - - def "PBS shouldn't exclude bidder when userFpdAvailable not match with condition"() { - given: "Bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - user = requestedUfpUser - } - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: USER_FPD_AVAILABLE)] - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - - where: - requestedUfpUser << [new User(data: null), new User(data: [null]), - new User(ext: new UserExt(data: null)), - new User(data: null, ext: new UserExt(data: null)) - ] - } - - def "PBS should exclude bidder when fpdAvailable match with condition"() { - given: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: FPD_AVAILABLE)] - } - - and: "Account with rule engine config" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - - where: - bidRequest << [ - getDefaultBidRequestWithMultiplyBidders().tap { - user = new User(data: [Data.defaultData]) - }, - getDefaultBidRequestWithMultiplyBidders().tap { - user = new User(ext: new UserExt(data: UserExtData.FPDUserExtData)) - }, - getDefaultBidRequestWithMultiplyBidders().tap { - site.content = new Content(data: [Data.defaultData]) - }, - getDefaultBidRequestWithMultiplyBidders().tap { - site.ext = new SiteExt(data: SiteExtData.FPDSiteExtData) - }, - getDefaultBidRequestWithMultiplyBidders(APP).tap { - app.content = new Content(data: [Data.defaultData]) - }, - getDefaultBidRequestWithMultiplyBidders(APP).tap { - app.ext = new AppExt(data: new AppExtData(language: PBSUtils.randomString)) - }, - ] - } - - def "PBS shouldn't exclude bidder when fpdAvailable not match with condition"() { - given: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: FPD_AVAILABLE)] - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - - where: - bidRequest << [ - getDefaultBidRequestWithMultiplyBidders().tap { - user = new User(data: null, ext: new UserExt(data: null)) - }, - getDefaultBidRequestWithMultiplyBidders().tap { - user = new User(ext: new UserExt(data: null)) - }, - getDefaultBidRequestWithMultiplyBidders().tap { - site.content = new Content(data: [null]) - site.ext = new SiteExt(data: null) - }, - getDefaultBidRequestWithMultiplyBidders().tap { - site.ext = new SiteExt(data: null) - }, - getDefaultBidRequestWithMultiplyBidders(APP).tap { - app.content = new Content(data: [null]) - app.ext = new AppExt(data: null) - }, - getDefaultBidRequestWithMultiplyBidders(APP).tap { - app.ext = new AppExt(data: null) - }, - ] - } - - def "PBS should exclude bidder when gppSidAvailable match with condition"() { - given: "Default bid request with multiply bidder" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - regs = new Regs(gppSid: [PBSUtils.getRandomEnum(GppSectionId).getIntValue()]) - } - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: GPP_SID_AVAILABLE)] - } - - and: "Account with rule engine config" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS shouldn't exclude bidder when gppSidAvailable not match with condition"() { - given: "Default bid request with multiply bidder" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - regs = new Regs(gppSid: gppSid) - } - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: GPP_SID_AVAILABLE)] - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - - where: - gppSid << [[PBSUtils.randomNegativeNumber], null] - } - - def "PBS should reject processing rule engine when gppSidIn schema function args contain invalid data"() { - given: "Test start time" - def startTime = Instant.now() - - and: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - regs = new Regs(gdpr: 0, gppSid: [PBSUtils.getRandomEnum(GppSectionId, [GppSectionId.TCF_EU_V2]).getIntValue()]) - } - - and: "Account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = GPP_SID_IN - it.args = new RuleEngineFunctionArgs(sids: [PBSUtils.randomString]) - } - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result shouldn't contain info about rule engine" - assert !getAnalyticResults(bidResponse) - - and: "Logs should contain error" - def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) - assert getLogsByText(logs, "Failed to parse rule-engine config for account ${bidRequest.accountId}: " + - "Function '${GPP_SID_IN}' configuration is invalid: " + - "Field 'sids' is required and has to be an array of integers").size() == 1 - } - - def "PBS should exclude bidder when gppSidIn match with condition"() { - given: "Default bid request with multiply bidder" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - regs = new Regs(gppSid: [gppSectionId.getIntValue()]) - } - - and: "Create rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = GPP_SID_IN - it.args = new RuleEngineFunctionArgs(sids: [gppSectionId]) - } - } - - and: "Save account with rule engine config" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved == groups.rules.first.results.first.args.bidders - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - - where: - gppSectionId << GppSectionId.values() - GppSectionId.TCF_EU_V2 - } - - def "PBS shouldn't exclude bidder when gppSidIn not match with condition"() { - given: "Default bid request with multiply bidder" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - regs = new Regs(gppSid: [gppSectionId.getIntValue()]) - } - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = GPP_SID_IN - it.args = new RuleEngineFunctionArgs(sids: [PBSUtils.getRandomEnum(GppSectionId, [gppSectionId]).getIntValue()]) - } - } - - and: "Save account with disabled or without rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - - where: - gppSectionId << GppSectionId.values() - GppSectionId.TCF_EU_V2 - } - - def "PBS should exclude bidder when tcfInScope match with condition"() { - given: "Default bid request with multiply bidder" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - regs = new Regs(gdpr: gdpr) - user = new User(ext: new UserExt(consent: new TcfConsent.Builder() - .setPurposesLITransparency(BASIC_ADS) - .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) - .build())) - } - - and: "Create rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema = [new RuleEngineModelSchema(function: TCF_IN_SCOPE)] - rules[0].conditions = [condition] - } - } - - and: "Save account with rule engine config" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved == groups.rules.first.results.first.args.bidders - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - - where: - gdpr | condition - 1 | 'true' - 0 | 'false' - } - - def "PBS shouldn't exclude bidder when tcfInScope not match with condition"() { - given: "Default bid request with multiply bidder" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - regs = new Regs(gdpr: gdpr) - user = new User(ext: new UserExt(consent: new TcfConsent.Builder() - .setPurposesLITransparency(BASIC_ADS) - .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) - .build())) - } - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema = [new RuleEngineModelSchema(function: TCF_IN_SCOPE)] - rules[0].conditions = [condition] - } - } - - and: "Save account with disabled or without rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Account cache" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - - where: - gdpr | condition - 0 | TRUE as String - 1 | FALSE as String - } - - def "PBS should reject processing rule engine when percent schema function args contain invalid data"() { - given: "Test start time" - def startTime = Instant.now() - - and: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = PERCENT - it.args = new RuleEngineFunctionArgs(percent: PBSUtils.randomString) - } - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "Analytics result should contain info about rule engine" - assert !getAnalyticResults(bidResponse) - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Logs should contain error" - def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) - assert getLogsByText(logs, "Function 'percent' configuration is invalid: " + - "Field 'pct' is required and has to be an integer") - } - - def "PBS should exclude bidder when percent match with condition"() { - given: "Default bid request with multiply bidder" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Create rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = PERCENT - it.args = new RuleEngineFunctionArgs(percent: PBSUtils.getRandomNumber(100)) - } - } - - and: "Save account with rule engine config" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS shouldn't exclude bidder when percent not match with condition"() { - given: "Default bid request with multiply bidder" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = PERCENT - it.args = new RuleEngineFunctionArgs(percent: percent) - } - } - - and: "Save account with disabled or without rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - - where: - percent << [0, PBSUtils.randomNegativeNumber] - } - - def "PBS should reject processing the rule engine when the prebidKey schema function contains incompatible arguments"() { - given: "Test start time" - def startTime = Instant.now() - - and: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = PREBID_KEY - it.args = new RuleEngineFunctionArgs(key: PBSUtils.randomNumber) - } - } - - and: "Account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBS response should not contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result shouldn't contain info about rule engine" - assert !getAnalyticResults(bidResponse) - - and: "Logs should contain error" - def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) - assert getLogsByText(logs, "Function 'prebidKey' configuration is invalid: " + - "Field 'key' is required and has to be a string") - } - - def "PBS should exclude bidder when prebidKey match with condition"() { - given: "Default bid request with multiply bidder" - def keyField = "key" - def keyString = PBSUtils.randomString - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - ext.prebid.keyValuePairs = [(keyString): keyField] - } - - and: "Create rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = PREBID_KEY - it.args = new RuleEngineFunctionArgs((keyField): keyString) - } - it.ruleSets[0].modelGroups[0].rules[0].conditions = [keyField] - } - - and: "Save account with rule engine config" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS shouldn't exclude bidder when prebidKey not match with condition"() { - given: "Default bid request with multiply bidder" - def key = PBSUtils.randomString - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - ext.prebid.keyValuePairs = [(key): PBSUtils.randomString] - } - - and: "Create rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = PREBID_KEY - it.args = new RuleEngineFunctionArgs(key: key) - } - it.ruleSets[0].modelGroups[0].rules[0].conditions = [PBSUtils.randomString] - } - - and: "Save account with disabled or without rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - } - - def "PBS should exclude bidder when domain match with condition"() { - given: "Default bid request with multiply bidder" - def randomDomain = PBSUtils.randomString - def bidRequest = getDefaultBidRequestWithMultiplyBidders(distributionChannel).tap { - updatePublisherDomain(it, distributionChannel, randomDomain) - } - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema = [new RuleEngineModelSchema(function: DOMAIN)] - rules[0].conditions = [randomDomain] - } - } - - and: "Save account with rule engine config" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - - where: - distributionChannel << [SITE, APP, DOOH] - } - - def "PBS shouldn't exclude bidder when domain not match with condition"() { - given: "Default bid request with random domain" - def bidRequest = getDefaultBidRequestWithMultiplyBidders(distributionCahannel).tap { - updatePublisherDomain(it, distributionCahannel, PBSUtils.randomString) - } - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema = [new RuleEngineModelSchema(function: DOMAIN)] - rules[0].conditions = [PBSUtils.randomString] - } - } - - and: "Save account with disabled or without rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - - where: - distributionCahannel << [SITE, APP, DOOH] - } - - def "PBS should reject processing the rule engine when the domainIn schema function contains incompatible arguments"() { - given: "Test start time" - def startTime = Instant.now() - - and: "Default bid request with multiplyB bidders" - def randomDomain = PBSUtils.randomString - def bidRequest = bidRequestClosure(randomDomain) - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = DOMAIN_IN - it.args = new RuleEngineFunctionArgs(domains: [PBSUtils.randomNumber]) - } - } - - and: "Save account with rule engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result shouldn't contain info about rule engine" - assert !getAnalyticResults(bidResponse) - - and: "Logs should contain error" - def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) - assert getLogsByText(logs, "Function 'domainIn' configuration is invalid: " + - "Field 'domains' is required and has to be an array of strings") - - where: - bidRequestClosure << [ - { String domain -> - getDefaultBidRequestWithMultiplyBidders(SITE).tap { - it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - } - }, - { String domain -> - getDefaultBidRequestWithMultiplyBidders(SITE).tap { - it.site.domain = domain - } - }, - { String domain -> - getDefaultBidRequestWithMultiplyBidders(APP).tap { - it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - } - }, - { String domain -> - getDefaultBidRequestWithMultiplyBidders(APP).tap { - it.app.domain = domain - } - }, - { String domain -> - getDefaultBidRequestWithMultiplyBidders(DOOH).tap { - it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - } - }, - { String domain -> - getDefaultBidRequestWithMultiplyBidders(DOOH).tap { - it.dooh.domain = domain - } - } - ] - } - - def "PBS should exclude bidder when domainIn match with condition"() { - given: "Default bid request with multiply bidder" - def randomDomain = PBSUtils.randomString - def bidRequest = bidRequestClosure(randomDomain) - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = DOMAIN_IN - it.args = new RuleEngineFunctionArgs(domains: [PBSUtils.randomString, randomDomain]) - } - } - - and: "Save account with rule engine config" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - - where: - bidRequestClosure << [ - { String domain -> - getDefaultBidRequestWithMultiplyBidders(SITE).tap { - it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) - } - }, - { String domain -> - getDefaultBidRequestWithMultiplyBidders(SITE).tap { - it.site.domain = domain - } - }, - { String domain -> - getDefaultBidRequestWithMultiplyBidders(APP).tap { - it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) - } - }, - { String domain -> - getDefaultBidRequestWithMultiplyBidders(APP).tap { - it.app.domain = domain - } - }, - { String domain -> - getDefaultBidRequestWithMultiplyBidders(DOOH).tap { - it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: domain) - } - }, - { String domain -> - getDefaultBidRequestWithMultiplyBidders(DOOH).tap { - it.dooh.domain = domain - } - } - ] - } - - def "PBS shouldn't exclude bidder when domainIn not match with condition"() { - given: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = DOMAIN_IN - it.args = new RuleEngineFunctionArgs(domains: [PBSUtils.randomString, PBSUtils.randomString]) - } - } - - and: "Save account with disabled or without rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - - where: - bidRequest << [ - getDefaultBidRequestWithMultiplyBidders(SITE).tap { - it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - }, - getDefaultBidRequestWithMultiplyBidders(SITE).tap { - it.site.domain = PBSUtils.randomString - }, - getDefaultBidRequestWithMultiplyBidders(APP).tap { - it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - }, - getDefaultBidRequestWithMultiplyBidders(APP).tap { - it.app.domain = PBSUtils.randomString - }, - getDefaultBidRequestWithMultiplyBidders(DOOH).tap { - it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - }, - getDefaultBidRequestWithMultiplyBidders(DOOH).tap { - it.dooh.domain = PBSUtils.randomString - }] - - } - - def "PBS should exclude bidder when bundle match with condition"() { - given: "Default bid request with multiply bidder" - def bundle = PBSUtils.randomString - def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { - app.bundle = bundle - } - - and: "Create rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema = [new RuleEngineModelSchema(function: BUNDLE)] - rules[0].conditions = [bundle] - } - } - - and: "Save account with rule engine config" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS shouldn't exclude bidder when bundle not match with condition"() { - given: "Default bid request with multiply bidder" - def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { - app.bundle = PBSUtils.randomString - } - - and: "Create rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema = [new RuleEngineModelSchema(function: BUNDLE)] - rules[0].conditions = [PBSUtils.randomString] - } - } - - and: "Save account with disabled or without rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - } - - def "PBS should reject processing the rule engine when the bundleIn schema function contains incompatible arguments"() { - given: "Test start time" - def startTime = Instant.now() - - and: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { - app.bundle = PBSUtils.randomString - } - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = BUNDLE_IN - it.args = new RuleEngineFunctionArgs(bundles: [PBSUtils.randomNumber]) - } - } - - and: "Save account with rule engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "Analytics result shouldn't contain info about rule engine" - assert !getAnalyticResults(bidResponse) - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - then: "Logs should contain error" - def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) - assert getLogsByText(logs, "Failed to parse rule-engine config for account ${bidRequest.accountId}: " + - "Function '${BUNDLE_IN}' configuration is invalid: " + - "Field 'bundles' is required and has to be an array of strings").size() == 1 - } - - def "PBS should exclude bidder when bundleIn match with condition"() { - given: "Default bid request with multiply bidders" - def bundle = PBSUtils.randomString - def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { - app.bundle = bundle - } - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = BUNDLE_IN - it.args = new RuleEngineFunctionArgs(bundles: [bundle]) - } - } - - and: "Save account with rule engine config" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Account cache" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS shouldn't exclude bidder when bundleIn not match with condition"() { - given: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { - app.bundle = PBSUtils.randomString - } - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = BUNDLE_IN - it.args = new RuleEngineFunctionArgs(bundles: [PBSUtils.randomString, PBSUtils.randomString]) - } - } - - and: "Save account with rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Account cache" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - } - - def "PBS should reject processing the rule engine when the mediaTypeIn schema function contains incompatible arguments"() { - given: "Test start time" - def startTime = Instant.now() - - and: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = MEDIA_TYPE_IN - it.args = new RuleEngineFunctionArgs(types: [mediaTypeInArgs]) - } - } - - and: "Save account with rule engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result shouldn't contain info about rule engine" - assert !getAnalyticResults(bidResponse) - - then: "Logs should contain error" - def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) - assert getLogsByText(logs, "Failed to parse rule-engine config for account ${bidRequest.accountId}: " + - "Function '${MEDIA_TYPE_IN}' configuration is invalid:" + - " Field 'types' is required and has to be an array of strings").size() == 1 - - where: - mediaTypeInArgs << [null, PBSUtils.randomNumber] - } - - def "PBS should exclude bidder when mediaTypeIn match with condition"() { - given: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Setup bidder response" - def bidderResponse = BidResponse.getDefaultBidResponse(bidRequest) - bidder.setResponse(bidRequest.id, bidderResponse) - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = MEDIA_TYPE_IN - it.args = new RuleEngineFunctionArgs(types: [BANNER.value]) - } - } - - and: "Save account with rule engine config" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS shouldn't exclude bidder when mediaTypeIn not match with condition"() { - given: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = MEDIA_TYPE_IN - it.args = new RuleEngineFunctionArgs(types: [PBSUtils.getRandomEnum(MediaType, [BANNER])]) - } - } - - and: "Save account with disabled or without rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - } - - def "PBS should exclude bidder when adUnitCode match with condition"() { - given: "Default bid request with multiply bidders" - def adUnitCode = PBSUtils.randomString - def bidRequest = bidRequestClosure(adUnitCode) - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema = [new RuleEngineModelSchema(function: AD_UNIT_CODE)] - rules[0].conditions = [adUnitCode] - } - } - - and: "Save storedImp into DB" - def storedImp = StoredImp.getStoredImp(bidRequest) - storedImpDao.save(storedImp) - - and: "Save account with rule engine config" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - - where: - bidRequestClosure << [ - { tagId -> - getDefaultBidRequestWithMultiplyBidders().tap { - imp[0].tagId = tagId - } - }, - { gpid -> - getDefaultBidRequestWithMultiplyBidders().tap { - imp[0].ext.gpid = gpid - } - }, - { pbAdSlot -> - getDefaultBidRequestWithMultiplyBidders().tap { - imp[0].ext.data = new ImpExtContextData(pbAdSlot: pbAdSlot) - } - }, - { storedRequestId -> - getDefaultBidRequestWithMultiplyBidders().tap { - imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: storedRequestId) - } - } - ] - } - - def "PBS shouldn't exclude bidder when adUnitCode not match with condition"() { - given: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema = [new RuleEngineModelSchema(function: AD_UNIT_CODE)] - rules[0].conditions = [PBSUtils.randomString] - } - } - - and: "Save account with disabled or without rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - } - - def "PBS should reject processing the rule engine when the adUnitCodeIn schema function contains incompatible arguments"() { - given: "Test start time" - def startTime = Instant.now() - - and: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - imp[0].tagId = PBSUtils.randomString - imp[0].ext.gpid = PBSUtils.randomString - imp[0].ext.data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) - imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomString) - } - - and: "Save storedImp into DB" - def storedImp = StoredImp.getStoredImp(bidRequest).tap { - impData = Imp.getDefaultImpression() - } - storedImpDao.save(storedImp) - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = AD_UNIT_CODE - it.args = new RuleEngineFunctionArgs(codes: [arguments]) - } - } - - and: "Save account with rule engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result shouldn't contain info about rule engine" - assert !getAnalyticResults(bidResponse) - - and: "Logs should contain error" - def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) - assert getLogsByText(logs, "Failed to parse rule-engine config for account ${bidRequest.accountId}: " + - "Function '${AD_UNIT_CODE}' configuration is invalid: No arguments allowed").size() == 1 - - where: - arguments << [PBSUtils.randomBoolean, PBSUtils.randomNumber] - } - - def "PBS should exclude bidder when adUnitCodeIn match with condition"() { - given: "Default bid request with multiply bidders" - def randomString = PBSUtils.randomString - def bidRequest = bidRequestClosure(randomString) - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = AD_UNIT_CODE_IN - it.args = new RuleEngineFunctionArgs(codes: [PBSUtils.randomString, randomString]) - } - } - - and: "Save storedImp into DB" - def storedImp = StoredImp.getStoredImp(bidRequest) - storedImpDao.save(storedImp) - - and: "Save account with rule engine config" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - - where: - bidRequestClosure << [ - { storedRequestId -> - getDefaultBidRequestWithMultiplyBidders().tap { - imp[0].tagId = PBSUtils.randomString - imp[0].ext.gpid = PBSUtils.randomString - imp[0].ext.data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) - imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: storedRequestId) - } - }, - { pbAdSlot -> - getDefaultBidRequestWithMultiplyBidders().tap { - imp[0].ext.gpid = PBSUtils.randomString - imp[0].ext.data = new ImpExtContextData(pbAdSlot: pbAdSlot) - imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomString) - } - }, - { gpid -> - getDefaultBidRequestWithMultiplyBidders().tap { - imp[0].tagId = PBSUtils.randomString - imp[0].ext.gpid = gpid - imp[0].ext.data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) - imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomString) - } - }, - { tagId -> - getDefaultBidRequestWithMultiplyBidders().tap { - imp[0].tagId = tagId - imp[0].ext.gpid = PBSUtils.randomString - imp[0].ext.data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) - imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomString) - } - } - ] - } - - def "PBS shouldn't exclude bidder when adUnitCodeIn not match with condition"() { - given: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders() - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = AD_UNIT_CODE_IN - it.args = new RuleEngineFunctionArgs(codes: [PBSUtils.randomString, PBSUtils.randomString]) - } - } - - and: "Save account with disabled or without rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - } - - def "PBS should exclude bidder when deviceType match with condition"() { - given: "Default bid request with multiply bidders" - def deviceType = PBSUtils.getRandomEnum(DeviceType) - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - device = new Device(devicetype: deviceType) - } - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema = [new RuleEngineModelSchema(function: DEVICE_TYPE)] - rules[0].conditions = [deviceType.value as String] - } - } - - and: "Save account with rule engine config" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS shouldn't exclude bidder when deviceType not match with condition"() { - given: "Default bid request with multiply bidders" - def requestDeviceType = PBSUtils.getRandomEnum(DeviceType) - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - device = new Device(devicetype: requestDeviceType) - } - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].tap { - schema = [new RuleEngineModelSchema(function: DEVICE_TYPE)] - rules[0].conditions = [PBSUtils.getRandomEnum(DeviceType, [requestDeviceType]).value as String] - } - } - - and: "Save account with disabled or without rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - } - - def "PBS should reject processing the rule engine when the deviceTypeIn schema function contains incompatible arguments"() { - given: "Test start time" - def startTime = Instant.now() - - and: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - device = new Device(devicetype: PBSUtils.getRandomEnum(DeviceType)) - } - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = DEVICE_COUNTRY_IN - it.args = new RuleEngineFunctionArgs(types: [PBSUtils.randomString]) - } - } - - and: "Save account with rule engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "PBs should perform bidder request" - assert bidder.getBidderRequests(bidRequest.id) - - and: "Bid response should contain all requested bidders" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result shouldn't contain info about rule engine" - assert !getAnalyticResults(bidResponse) - - and: "Logs should contain error" - def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) - assert getLogsByText(logs, "Function '${DEVICE_COUNTRY_IN}' configuration is invalid: " + - "Field 'countries' is required and has to be an array of strings") - } - - def "PBS should exclude bidder when deviceTypeIn match with condition"() { - given: "Default bid request with multiply bidders" - def deviceType = PBSUtils.getRandomEnum(DeviceType) - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - device = new Device(devicetype: deviceType) - } - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = DEVICE_TYPE_IN - it.args = new RuleEngineFunctionArgs(types: [deviceType.value]) - } - } - - and: "Save account with rule engine config" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "Analytics result should contain info about name and status" - def analyticsResult = getAnalyticResults(bidResponse) - def result = analyticsResult[0] - assert result.name == ModuleName.PB_RULE_ENGINE.code - assert result.status == SUCCESS - - and: "Analytics result detail info" - def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { - it.status == SUCCESS - it.values.analyticsKey == groups.analyticsKey - it.values.modelVersion == groups.version - it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue - it.values.resultFunction == groups.rules.first.results.first.function.value - it.values.conditionFired == groups.rules.first.conditions.first - it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() - it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - it.appliedTo.impIds == bidRequest.imp.id - } - - and: "Response should seatNon bid with code 203" - assert bidResponse.ext.seatnonbid.size() == 1 - def seatNonBid = bidResponse.ext.seatnonbid[0] - assert seatNonBid.seat == OPENX - assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id - assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE - } - - def "PBS shouldn't exclude bidder when deviceTypeIn not match with condition"() { - given: "Default bid request with multiply bidders" - def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - device = new Device(devicetype: PBSUtils.getRandomEnum(DeviceType)) - } - - and: "Create account with rule engine config" - def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].schema[0].tap { - it.function = DEVICE_TYPE_IN - it.args = new RuleEngineFunctionArgs(types: [PBSUtils.getRandomEnum(DeviceType).value as String]) - } - } - - and: "Save account with disabled or without rules engine" - def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) - accountDao.save(accountWithRulesEngine) - - and: "Cache account" - pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - when: "PBS processes auction request" - def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) - - then: "Bid response should contain seats" - assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS - - and: "PBs should perform bidder requests" - assert bidder.getBidderRequests(bidRequest.id) - - and: "PBS should not contain errors, warnings" - assert !bidResponse.ext?.warnings - assert !bidResponse.ext?.errors - - and: "PBS response shouldn't contain seatNonBid" - assert !bidResponse.ext.seatnonbid - - and: "Analytics result shouldn't contain info about module exclude" - assert !getAnalyticResults(bidResponse) - } - - private static BidRequest updatePublisherDomain(BidRequest bidRequest, DistributionChannel distributionChannel, String domain) { - if (distributionChannel == SITE) { - bidRequest.tap { - it.site.publisher.domain = domain - } - } - if (distributionChannel == APP) { - bidRequest.tap { - it.app.publisher.domain = domain - } - } - if (distributionChannel == DOOH) { - bidRequest.tap { - it.dooh.publisher.domain = domain - } - } - } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineDeviceSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineDeviceSpec.groovy new file mode 100644 index 00000000000..d8eabbaf60d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineDeviceSpec.groovy @@ -0,0 +1,434 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.config.RuleEngineFunctionArgs +import org.prebid.server.functional.model.config.RuleEngineModelSchema +import org.prebid.server.functional.model.pricefloors.Country +import org.prebid.server.functional.model.request.auction.Device +import org.prebid.server.functional.model.request.auction.DeviceType +import org.prebid.server.functional.util.PBSUtils + +import java.time.Instant + +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_TYPE +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_TYPE_IN +import static org.prebid.server.functional.model.pricefloors.Country.USA +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + +class RuleEngineDeviceSpec extends RuleEngineBaseSpec { + + def "PBS should exclude bidder when deviceCountry match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DEVICE_COUNTRY)] + rules[0].conditions = [USA.toString()] + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when deviceCountry not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema[0].function = DEVICE_COUNTRY + rules[0].conditions = [PBSUtils.getRandomEnum(Country, [USA]).toString()] + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Account cache" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should exclude bidder when deviceType match with condition"() { + given: "Default bid request with multiply bidders" + def deviceType = PBSUtils.getRandomEnum(DeviceType) + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + device = new Device(devicetype: deviceType) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DEVICE_TYPE)] + rules[0].conditions = [deviceType.value as String] + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when deviceType not match with condition"() { + given: "Default bid request with multiply bidders" + def requestDeviceType = PBSUtils.getRandomEnum(DeviceType) + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + device = new Device(devicetype: requestDeviceType) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DEVICE_TYPE)] + rules[0].conditions = [PBSUtils.getRandomEnum(DeviceType, [requestDeviceType]).value as String] + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should reject processing the rule engine when the deviceCountryIn schema function contains incompatible arguments"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + device = new Device(devicetype: PBSUtils.getRandomEnum(DeviceType)) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DEVICE_COUNTRY_IN + it.args = new RuleEngineFunctionArgs(types: [PBSUtils.randomString]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING(bidRequest.accountId, DEVICE_COUNTRY_IN)) + } + + def "PBS should reject processing the rule engine when the deviceTypeIn schema function contains incompatible arguments"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + device = new Device(devicetype: PBSUtils.getRandomEnum(DeviceType)) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DEVICE_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: [PBSUtils.getRandomString()]) + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_INTEGERS_LOG_WARNING(bidRequest.accountId, DEVICE_TYPE_IN)) + } + + def "PBS should exclude bidder when deviceTypeIn match with condition"() { + given: "Default bid request with multiply bidders" + def deviceType = PBSUtils.getRandomEnum(DeviceType) + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + device = new Device(devicetype: deviceType) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DEVICE_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: [deviceType.value]) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when deviceTypeIn not match with condition"() { + given: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + device = new Device(devicetype: PBSUtils.getRandomEnum(DeviceType)) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DEVICE_TYPE_IN + it.args = new RuleEngineFunctionArgs(types: [PBSUtils.getRandomEnum(DeviceType).value as String]) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineInfrastructureSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineInfrastructureSpec.groovy new file mode 100644 index 00000000000..542cad335ca --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineInfrastructureSpec.groovy @@ -0,0 +1,264 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.config.RuleEngineFunctionArgs +import org.prebid.server.functional.model.config.RuleEngineModelSchema +import org.prebid.server.functional.util.PBSUtils + +import java.time.Instant + +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.RuleEngineFunction.DATA_CENTER +import static org.prebid.server.functional.model.config.RuleEngineFunction.DATA_CENTER_IN +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + +class RuleEngineInfrastructureSpec extends RuleEngineBaseSpec { + + def "PBS should reject processing rule engine when dataCenterIn schema function args contain invalid data"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DATA_CENTER_IN + it.args = new RuleEngineFunctionArgs(countries: [CONFIG_DATA_CENTER]) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING(bidRequest.accountId, DATA_CENTER_IN)) + } + + def "PBS should exclude bidder when dataCenterIn match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DATA_CENTER_IN + it.args = new RuleEngineFunctionArgs(datacenters: [CONFIG_DATA_CENTER]) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when dataCentersIn not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = DATA_CENTER_IN + it.args = new RuleEngineFunctionArgs(datacenters: [PBSUtils.randomString]) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should exclude bidder when dataCenter match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DATA_CENTER)] + rules[0].conditions = [CONFIG_DATA_CENTER] + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when dataCenter not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: DATA_CENTER)] + rules[0].conditions = [PBSUtils.randomString] + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy new file mode 100644 index 00000000000..2a145c7ac8d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy @@ -0,0 +1,909 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.config.RuleEngineFunctionArgs +import org.prebid.server.functional.model.config.RuleEngineModelSchema +import org.prebid.server.functional.model.request.GppSectionId +import org.prebid.server.functional.model.request.auction.AppExt +import org.prebid.server.functional.model.request.auction.AppExtData +import org.prebid.server.functional.model.request.auction.Content +import org.prebid.server.functional.model.request.auction.Data +import org.prebid.server.functional.model.request.auction.Eid +import org.prebid.server.functional.model.request.auction.Regs +import org.prebid.server.functional.model.request.auction.SiteExt +import org.prebid.server.functional.model.request.auction.SiteExtData +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.util.PBSUtils +import org.prebid.server.functional.util.privacy.TcfConsent + +import java.time.Instant + +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.RuleEngineFunction.EID_AVAILABLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.EID_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.FPD_AVAILABLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.GPP_SID_AVAILABLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.GPP_SID_IN +import static org.prebid.server.functional.model.config.RuleEngineFunction.TCF_IN_SCOPE +import static org.prebid.server.functional.model.config.RuleEngineFunction.USER_FPD_AVAILABLE +import static org.prebid.server.functional.model.request.auction.DistributionChannel.APP +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE +import static org.prebid.server.functional.util.privacy.TcfConsent.GENERIC_VENDOR_ID +import static org.prebid.server.functional.util.privacy.TcfConsent.PurposeId.BASIC_ADS + +class RuleEnginePrivacySpec extends RuleEngineBaseSpec { + + def "PBS should exclude bidder when eidAvailable match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + user = new User(eids: [Eid.getDefaultEid()]) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: EID_AVAILABLE)] + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when eidAvailable not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + user = new User(eids: eids) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema[0].function = EID_AVAILABLE + rules[0].conditions = ["TRUE"] + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + eids << [null, []] + } + + def "PBS should reject processing rule engine when eidIn schema function args contain invalid data"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = EID_IN + it.args = new RuleEngineFunctionArgs(sources: [PBSUtils.randomNumber]) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING(bidRequest.accountId, EID_IN)) + } + + def "PBS should exclude bidder when eidIn match with condition"() { + given: "Bid request with multiply bidders" + def eid = Eid.getDefaultEid() + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + user = new User(eids: [eid]) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = EID_IN + it.args = new RuleEngineFunctionArgs(sources: [PBSUtils.randomString, eid.source, PBSUtils.randomString]) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when eidIn match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + user = new User(eids: [Eid.getDefaultEid()]) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = EID_IN + it.args = new RuleEngineFunctionArgs(sources: [PBSUtils.randomString, PBSUtils.randomString]) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + + def "PBS should exclude bidder when userFpdAvailable match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + user = requestedUfpUser + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: USER_FPD_AVAILABLE)] + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + requestedUfpUser << [new User(data: [Data.defaultData], ext: new UserExt(data: UserExtData.FPDUserExtData)), + new User(ext: new UserExt(data: UserExtData.FPDUserExtData)), + new User(data: [Data.defaultData])] + } + + def "PBS shouldn't exclude bidder when userFpdAvailable not match with condition"() { + given: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + user = requestedUfpUser + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: USER_FPD_AVAILABLE)] + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + requestedUfpUser << [new User(data: null), new User(data: [null]), + new User(ext: new UserExt(data: null)), + new User(data: null, ext: new UserExt(data: null)) + ] + } + + def "PBS should exclude bidder when fpdAvailable match with condition"() { + given: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: FPD_AVAILABLE)] + } + + and: "Account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + bidRequest << [ + getDefaultBidRequestWithMultiplyBidders().tap { + user = new User(data: [Data.defaultData]) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + user = new User(ext: new UserExt(data: UserExtData.FPDUserExtData)) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + site.content = new Content(data: [Data.defaultData]) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + site.ext = new SiteExt(data: SiteExtData.FPDSiteExtData) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.content = new Content(data: [Data.defaultData]) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.ext = new AppExt(data: new AppExtData(language: PBSUtils.randomString)) + }, + ] + } + + def "PBS shouldn't exclude bidder when fpdAvailable not match with condition"() { + given: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: FPD_AVAILABLE)] + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + bidRequest << [ + getDefaultBidRequestWithMultiplyBidders().tap { + user = new User(data: null, ext: new UserExt(data: null)) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + user = new User(ext: new UserExt(data: null)) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + site.content = new Content(data: [null]) + site.ext = new SiteExt(data: null) + }, + getDefaultBidRequestWithMultiplyBidders().tap { + site.ext = new SiteExt(data: null) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.content = new Content(data: [null]) + app.ext = new AppExt(data: null) + }, + getDefaultBidRequestWithMultiplyBidders(APP).tap { + app.ext = new AppExt(data: null) + }, + ] + } + + def "PBS should exclude bidder when gppSidAvailable match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + regs = new Regs(gppSid: [PBSUtils.getRandomEnum(GppSectionId).getIntValue()]) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: GPP_SID_AVAILABLE)] + } + + and: "Account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when gppSidAvailable not match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + regs = new Regs(gppSid: gppSid) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema = [new RuleEngineModelSchema(function: GPP_SID_AVAILABLE)] + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + gppSid << [[PBSUtils.randomNegativeNumber], null] + } + + def "PBS should reject processing rule engine when gppSidIn schema function args contain invalid data"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + regs = new Regs(gdpr: 0, gppSid: [PBSUtils.getRandomEnum(GppSectionId, [GppSectionId.TCF_EU_V2]).getIntValue()]) + } + + and: "Account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = GPP_SID_IN + it.args = new RuleEngineFunctionArgs(sids: [PBSUtils.randomString]) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_INTEGERS_LOG_WARNING(bidRequest.accountId, GPP_SID_IN)) + } + + def "PBS should exclude bidder when gppSidIn match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + regs = new Regs(gppSid: [gppSectionId.getIntValue()]) + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = GPP_SID_IN + it.args = new RuleEngineFunctionArgs(sids: [gppSectionId]) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved == groups.rules.first.results.first.args.bidders + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + gppSectionId << GppSectionId.values() - GppSectionId.TCF_EU_V2 + } + + def "PBS shouldn't exclude bidder when gppSidIn not match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + regs = new Regs(gppSid: [gppSectionId.getIntValue()]) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = GPP_SID_IN + it.args = new RuleEngineFunctionArgs(sids: [PBSUtils.getRandomEnum(GppSectionId, [gppSectionId]).getIntValue()]) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + gppSectionId << GppSectionId.values() - GppSectionId.TCF_EU_V2 + } + + def "PBS should exclude bidder when tcfInScope match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + regs = new Regs(gdpr: gdpr) + user = new User(ext: new UserExt(consent: new TcfConsent.Builder() + .setPurposesLITransparency(BASIC_ADS) + .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .build())) + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: TCF_IN_SCOPE)] + rules[0].conditions = [condition] + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved == groups.rules.first.results.first.args.bidders + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + + where: + gdpr | condition + 1 | 'true' + 0 | 'false' + } + + def "PBS shouldn't exclude bidder when tcfInScope not match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + regs = new Regs(gdpr: gdpr) + user = new User(ext: new UserExt(consent: new TcfConsent.Builder() + .setPurposesLITransparency(BASIC_ADS) + .setVendorLegitimateInterest([GENERIC_VENDOR_ID]) + .build())) + } + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + schema = [new RuleEngineModelSchema(function: TCF_IN_SCOPE)] + rules[0].conditions = [condition] + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Account cache" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + gdpr | condition + 0 | "TRUE" + 1 | "FALSE" + } +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpecialSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpecialSpec.groovy new file mode 100644 index 00000000000..f8c47e4e13e --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSpecialSpec.groovy @@ -0,0 +1,319 @@ +package org.prebid.server.functional.tests.module.pbruleengine + +import org.prebid.server.functional.model.config.RuleEngineFunctionArgs +import org.prebid.server.functional.util.PBSUtils + +import java.time.Instant + +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE +import static org.prebid.server.functional.model.bidder.BidderName.AMX +import static org.prebid.server.functional.model.bidder.BidderName.GENERIC +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.RuleEngineFunction.PERCENT +import static org.prebid.server.functional.model.config.RuleEngineFunction.PREBID_KEY +import static org.prebid.server.functional.model.request.auction.FetchStatus.SUCCESS +import static org.prebid.server.functional.model.response.auction.BidRejectionReason.REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + +class RuleEngineSpecialSpec extends RuleEngineBaseSpec { + + def "PBS should reject processing rule engine when percent schema function args contain invalid data"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PERCENT + it.args = new RuleEngineFunctionArgs(percent: PBSUtils.randomString) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "Analytics result should contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_SINGLE_INTEGER_LOG_WARNING(bidRequest.accountId, PERCENT)) + } + + def "PBS should exclude bidder when percent match with condition"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PERCENT + it.args = new RuleEngineFunctionArgs(percent: PBSUtils.getRandomNumber(100)) + } + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when percent less than zero"() { + given: "Default bid request with multiply bidder" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PERCENT + it.args = new RuleEngineFunctionArgs(percent: percent) + } + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + where: + percent << [0, PBSUtils.randomNegativeNumber] + } + + def "PBS should reject processing the rule engine when the prebidKey schema function contains incompatible arguments"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PREBID_KEY + it.args = new RuleEngineFunctionArgs(key: PBSUtils.randomNumber) + } + } + + and: "Account with rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBS response should not contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_SINGLE_STRING_LOG_WARNING(bidRequest.accountId, PREBID_KEY)) + } + + def "PBS should exclude bidder when prebidKey match with condition"() { + given: "Default bid request with multiply bidder" + def keyField = "key" + def keyString = PBSUtils.randomString + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + ext.prebid.keyValuePairs = [(keyString): keyField] + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PREBID_KEY + it.args = new RuleEngineFunctionArgs((keyField): keyString) + } + it.ruleSets[0].modelGroups[0].rules[0].conditions = [keyField] + } + + and: "Save account with rule engine config" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == [GENERIC, AMX].sort() + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Analytics result should contain info about name and status" + def analyticsResult = getAnalyticResults(bidResponse) + def result = analyticsResult[0] + assert result.name == PB_RULE_ENGINE.code + assert result.status == SUCCESS + + and: "Analytics result detail info" + def groups = pbRuleEngine.ruleSets[0].modelGroups[0] + verifyAll(result.results[0]) { + it.status == SUCCESS + it.values.analyticsKey == groups.analyticsKey + it.values.modelVersion == groups.version + it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue + it.values.resultFunction == groups.rules.first.results.first.function.value + it.values.conditionFired == groups.rules.first.conditions.first + it.values.biddersRemoved.sort() == groups.rules.first.results.first.args.bidders.sort() + it.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + it.appliedTo.impIds == bidRequest.imp.id + } + + and: "Response should seatNon bid with code 203" + assert bidResponse.ext.seatnonbid.size() == 1 + def seatNonBid = bidResponse.ext.seatnonbid[0] + assert seatNonBid.seat == OPENX + assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id + assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE + } + + def "PBS shouldn't exclude bidder when prebidKey not match with condition"() { + given: "Default bid request with multiply bidder" + def key = PBSUtils.randomString + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + ext.prebid.keyValuePairs = [(key): PBSUtils.randomString] + } + + and: "Create rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = PREBID_KEY + it.args = new RuleEngineFunctionArgs(key: key) + } + it.ruleSets[0].modelGroups[0].rules[0].conditions = [PBSUtils.randomString] + } + + and: "Save account with disabled or without rules engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + } + +} diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy index 135c92c1d1d..eb4f57707bd 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineSyncSpec.groovy @@ -23,15 +23,13 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = true + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC, true)] } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) and: "Cookies headers" - def uidsCookie = UidsCookie.defaultUidsCookie - def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) + def cookieHeader = HttpUtil.getCookieHeader(UidsCookie.defaultUidsCookie) and: "Cache account" pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -82,15 +80,13 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = false + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC, false)] } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) and: "Cookies headers" - def uidsCookie = UidsCookie.defaultUidsCookie - def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) + def cookieHeader = HttpUtil.getCookieHeader(UidsCookie.defaultUidsCookie) and: "Cache account" pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -123,8 +119,7 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = true + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithExcludeResult(GENERIC, true)] } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) @@ -160,15 +155,13 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = true + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC, true)] } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) and: "Cookies headers" - def uidsCookie = UidsCookie.defaultUidsCookie - def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) + def cookieHeader = HttpUtil.getCookieHeader(UidsCookie.defaultUidsCookie) and: "Cache account" pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -222,15 +215,13 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = false + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC, false)] } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) and: "Cookies headers" - def uidsCookie = UidsCookie.defaultUidsCookie - def cookieHeader = HttpUtil.getCookieHeader(uidsCookie) + def cookieHeader = HttpUtil.getCookieHeader(UidsCookie.defaultUidsCookie) and: "Cache account" pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) @@ -280,8 +271,7 @@ class RuleEngineSyncSpec extends RuleEngineBaseSpec { and: "Account with rules sets" def pbRuleEngine = createRulesEngineWithRule().tap { - it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC)] - it.ruleSets[0].modelGroups[0].rules[0].results[0].args.ifSyncedId = true + it.ruleSets[0].modelGroups[0].rules[0].results = [createRuleEngineModelRuleWithIncludeResult(GENERIC, true)] } def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) accountDao.save(accountWithRulesEngine) diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy index 177c4155abc..f3271297895 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineValidationSpec.groovy @@ -1,8 +1,22 @@ package org.prebid.server.functional.tests.module.pbruleengine +import org.prebid.server.functional.model.config.RuleEngineFunctionArgs +import org.prebid.server.functional.util.PBSUtils + import java.time.Instant import static org.prebid.server.functional.model.config.PbRulesEngine.createRulesEngineWithRule +import static org.prebid.server.functional.model.config.RuleEngineFunction.AD_UNIT_CODE +import static org.prebid.server.functional.model.config.RuleEngineFunction.BUNDLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.CHANNEL +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_COUNTRY +import static org.prebid.server.functional.model.config.RuleEngineFunction.DEVICE_TYPE +import static org.prebid.server.functional.model.config.RuleEngineFunction.DOMAIN +import static org.prebid.server.functional.model.config.RuleEngineFunction.EID_AVAILABLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.FPD_AVAILABLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.GPP_SID_AVAILABLE +import static org.prebid.server.functional.model.config.RuleEngineFunction.TCF_IN_SCOPE +import static org.prebid.server.functional.model.config.RuleEngineFunction.USER_FPD_AVAILABLE import static org.prebid.server.functional.model.pricefloors.Country.BULGARIA class RuleEngineValidationSpec extends RuleEngineBaseSpec { @@ -174,7 +188,8 @@ class RuleEngineValidationSpec extends RuleEngineBaseSpec { and: "PBs should emit failed logs" def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) - assert getLogsByText(logs, "Failed to parse rule-engine config for account $bidRequest.accountId: Weighted list cannot be empty").size() == 1 + assert getLogsByText(logs, "Failed to parse rule-engine config for account $bidRequest.accountId:" + + " Weighted list cannot be empty") } def "PBS shouldn't log default model when rule does not fired and empty model default"() { @@ -317,4 +332,103 @@ class RuleEngineValidationSpec extends RuleEngineBaseSpec { assert getLogsByText(logs, "Parsing rule for account $bidRequest.accountId").size() == 1 } + def "PBS shouldn't take rule with higher weight and not remove bidder when weight negative or zero"() { + given: "Start up time" + def start = Instant.now() + + and: "Bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { + updateBidRequestWithGeoCountry(it) + } + + and: "Account with few model group" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].tap { + it.weight = weight + } + } + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "Bid response should contain seats" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "PBs should perform bidder requests" + assert bidder.getBidderRequests(bidRequest.id) + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "Analytics result shouldn't contain info about module exclude" + assert !getAnalyticResults(bidResponse) + + and: "PBS should emit log" + def logsByTime = pbsServiceWithRulesEngineModule.getLogsByTime(start) + assert getLogsByText(logsByTime, "Failed to parse rule-engine config for account $bidRequest.accountId:" + + " Weight must be greater than zero") + + where: + weight << [PBSUtils.randomNegativeNumber, 0] + } + + def "PBS should reject processing rule engine when #function schema function contain args"() { + given: "Test start time" + def startTime = Instant.now() + + and: "Default bid request with multiply bidders" + def bidRequest = getDefaultBidRequestWithMultiplyBidders() + + and: "Create account with rule engine config" + def pbRuleEngine = createRulesEngineWithRule().tap { + it.ruleSets[0].modelGroups[0].schema[0].tap { + it.function = function + it.args = RuleEngineFunctionArgs.defaultFunctionArgs + } + } + + and: "Save account with rule engine" + def accountWithRulesEngine = getAccountWithRulesEngine(bidRequest.accountId, pbRuleEngine) + accountDao.save(accountWithRulesEngine) + + and: "Cache account" + pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + when: "PBS processes auction request" + def bidResponse = pbsServiceWithRulesEngineModule.sendAuctionRequest(bidRequest) + + then: "PBs should perform bidder request" + assert bidder.getBidderRequests(bidRequest.id) + + and: "Bid response should contain all requested bidders" + assert bidResponse.seatbid.seat.sort() == MULTI_BID_ADAPTERS + + and: "Analytics result shouldn't contain info about rule engine" + assert !getAnalyticResults(bidResponse) + + and: "PBS response shouldn't contain seatNonBid" + assert !bidResponse.ext.seatnonbid + + and: "PBS should not contain errors, warnings" + assert !bidResponse.ext?.warnings + assert !bidResponse.ext?.errors + + and: "Logs should contain error" + def logs = pbsServiceWithRulesEngineModule.getLogsByTime(startTime) + assert getLogsByText(logs, "Failed to parse rule-engine config for account ${bidRequest.accountId}: " + + "Function '${function.value}' configuration is invalid: No arguments allowed") + + where: + function << [DEVICE_TYPE, AD_UNIT_CODE, BUNDLE, DOMAIN, TCF_IN_SCOPE, GPP_SID_AVAILABLE, FPD_AVAILABLE, + USER_FPD_AVAILABLE, EID_AVAILABLE, CHANNEL, DEVICE_COUNTRY] + } } From 67252a99a2ae122f5a33ba2fe09f337a56f82174 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Sun, 5 Oct 2025 18:27:43 +0300 Subject: [PATCH 18/18] some minor updates --- .../model/config/RuleEngineFunction.groovy | 8 +- .../tests/module/ModuleBaseSpec.groovy | 4 - .../pbruleengine/RuleEngineAliasSpec.groovy | 30 +++----- .../pbruleengine/RuleEngineBaseSpec.groovy | 74 ++++++++----------- .../pbruleengine/RuleEngineContextSpec.groovy | 51 +++++-------- .../pbruleengine/RuleEngineCoreSpec.groovy | 68 +++++++---------- .../pbruleengine/RuleEnginePrivacySpec.groovy | 8 +- 7 files changed, 94 insertions(+), 149 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy index d7e5845bc07..b67b4dac362 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/RuleEngineFunction.groovy @@ -23,10 +23,10 @@ enum RuleEngineFunction { BUNDLE("bundle", null), BUNDLE_IN("bundleIn", "bundles"), MEDIA_TYPE_IN("mediaTypeIn", "types"), - AD_UNIT_CODE("adUnitCode",null), - AD_UNIT_CODE_IN("adUnitCodeIn","codes"), - DEVICE_TYPE("deviceType",null), - DEVICE_TYPE_IN("deviceTypeIn","types"), + AD_UNIT_CODE("adUnitCode", null), + AD_UNIT_CODE_IN("adUnitCodeIn", "codes"), + DEVICE_TYPE("deviceType", null), + DEVICE_TYPE_IN("deviceTypeIn", "types") private String value private String fieldName diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy index c1ecc250c78..f721b93eac0 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/ModuleBaseSpec.groovy @@ -81,8 +81,4 @@ class ModuleBaseSpec extends BaseSpec { ?.outcomes?.first()?.groups?.first() ?.invocationResults?.first()?.analyticsTags?.activities } - - protected static List getInvocationResult(BidResponse response) { - response.ext.prebid.modules?.trace?.stages?.first()?.outcomes?.first()?.groups?.first()?.invocationResults - } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy index af43209a87f..64a42194b59 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineAliasSpec.groovy @@ -2,7 +2,7 @@ package org.prebid.server.functional.tests.module.pbruleengine import org.prebid.server.functional.model.request.auction.Imp -import static org.prebid.server.functional.model.ModuleName.* +import static org.prebid.server.functional.model.ModuleName.PB_RULE_ENGINE import static org.prebid.server.functional.model.bidder.BidderName.ALIAS import static org.prebid.server.functional.model.bidder.BidderName.AMX import static org.prebid.server.functional.model.bidder.BidderName.GENERIC @@ -20,10 +20,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { given: "Bid request with multiply imps bidders" def bidders = [OPENX, AMX, OPENX_ALIAS, GENERIC] def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(Imp.defaultImpression) - it.imp[1].tap { - updateBidderImp(it, bidders) - } + it.imp.add(updateBidderImp(Imp.defaultImpression, bidders)) updateBidRequestWithGeoCountry(it) } @@ -69,7 +66,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue impResult.values.resultFunction == groups.rules.first.results.first.function.value impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == [OPENX, AMX, GENERIC].sort() + impResult.values.biddersRemoved.sort() == MULTI_BID_ADAPTERS.sort() impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE impResult.appliedTo.impIds == bidRequest.imp.id } @@ -77,7 +74,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { and: "Response should populate seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 3 def seatNonBid = bidResponse.ext.seatnonbid - assert seatNonBid.seat.sort() == [OPENX, AMX, GENERIC].sort() + assert seatNonBid.seat.sort() == MULTI_BID_ADAPTERS.sort() assert seatNonBid.nonBid.impId.flatten().unique().sort() == bidRequest.imp.id.sort() assert seatNonBid.nonBid.statusCode.flatten().unique() == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE] } @@ -85,10 +82,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { def "PBS should remove hard alias bidder from imps when hard alias bidder excluded in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(Imp.defaultImpression) - it.imp[1].tap { - updateBidderImp(it, [OPENX, OPENX_ALIAS, AMX]) - } + it.imp.add(updateBidderImp(Imp.defaultImpression, [OPENX, OPENX_ALIAS, AMX])) updateBidRequestWithGeoCountry(it) } @@ -147,10 +141,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { def "PBS should leave only soft alias bidder at imps when soft alias bidder include in account config"() { given: "Bid request with multiply imps bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(Imp.defaultImpression) - it.imp[1].tap { - updateBidderImp(it, [ALIAS, AMX, OPENX]) - } + it.imp.add(updateBidderImp(Imp.defaultImpression, [ALIAS, AMX, OPENX])) ext.prebid.aliases = [(ALIAS.value): GENERIC] updateBidRequestWithGeoCountry(it) } @@ -194,7 +185,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { impResult.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue impResult.values.resultFunction == groups.rules.first.results.first.function.value impResult.values.conditionFired == groups.rules.first.conditions.first - impResult.values.biddersRemoved.sort() == [OPENX, GENERIC, AMX].sort() + impResult.values.biddersRemoved.sort() == MULTI_BID_ADAPTERS.sort() impResult.values.seatNonBid == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE impResult.appliedTo.impIds == bidRequest.imp.id } @@ -202,7 +193,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { and: "Response should populate seatNon bid with code 203" assert bidResponse.ext.seatnonbid.size() == 3 def seatNonBid = bidResponse.ext.seatnonbid - assert seatNonBid.seat.sort() == [OPENX, GENERIC, AMX].sort() + assert seatNonBid.seat.sort() == MULTI_BID_ADAPTERS.sort() assert seatNonBid.nonBid.impId.flatten().unique().sort() == bidRequest.imp.id.sort() assert seatNonBid.nonBid.statusCode.unique().flatten() == [REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE, REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE, @@ -212,10 +203,7 @@ class RuleEngineAliasSpec extends RuleEngineBaseSpec { def "PBS should remove soft alias bidder from imps when soft alias bidder excluded in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(Imp.defaultImpression) - it.imp[1].tap { - updateBidderImp(it, [ALIAS, AMX, OPENX]) - } + it.imp.add(updateBidderImp(Imp.defaultImpression, [ALIAS, AMX, OPENX])) ext.prebid.aliases = [(ALIAS.value): GENERIC] updateBidRequestWithGeoCountry(it) } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy index b180e9a01af..57f8659682b 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineBaseSpec.groovy @@ -97,7 +97,7 @@ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { } } - protected static void updateBidderImp(Imp imp, List bidders = MULTI_BID_ADAPTERS) { + protected static Imp updateBidderImp(Imp imp, List bidders = MULTI_BID_ADAPTERS) { imp.ext.prebid.bidder.tap { openx = bidders.contains(OPENX) ? Openx.defaultOpenx : null openxAlias = bidders.contains(OPENX_ALIAS) ? Openx.defaultOpenx : null @@ -105,13 +105,14 @@ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { generic = bidders.contains(GENERIC) ? new Generic() : null alias = bidders.contains(ALIAS) ? new Generic() : null } + imp } protected static void updateBidRequestWithGeoCountry(BidRequest bidRequest, Country country = USA) { bidRequest.device = new Device(geo: new Geo(country: country)) } - protected static getAccountWithRulesEngine(String accountId, PbRulesEngine ruleEngine) { + protected static Account getAccountWithRulesEngine(String accountId, PbRulesEngine ruleEngine) { def accountHooksConfiguration = new AccountHooksConfiguration(modules: new PbsModulesConfig(pbRuleEngine: ruleEngine)) new Account(uuid: accountId, config: new AccountConfig(hooks: accountHooksConfiguration)) } @@ -137,52 +138,41 @@ abstract class RuleEngineBaseSpec extends ModuleBaseSpec { } protected static BidRequest updatePublisherDomain(BidRequest bidRequest, DistributionChannel distributionChannel, String domain) { - if (distributionChannel == SITE) { - bidRequest.tap { - it.site.publisher.domain = domain - } - } - if (distributionChannel == APP) { - bidRequest.tap { - it.app.publisher.domain = domain - } - } - if (distributionChannel == DOOH) { - bidRequest.tap { - it.dooh.publisher.domain = domain - } + switch (distributionChannel) { + case SITE: + bidRequest.site.publisher.domain = domain + break + case APP: + bidRequest.app.publisher.domain = domain + break + case DOOH: + bidRequest.dooh.publisher.domain = domain + break } + bidRequest } - protected static String getImpAdUnitCodeByCode(Imp imp, ImpUnitCode impUnitCode) { - if (TAG_ID == impUnitCode) { - return imp.tagId + protected static String getImpAdUnitCodeByCode(Imp imp, ImpUnitCode code) { + switch (code) { + case TAG_ID: + return imp.tagId + case GPID: + return imp.ext.gpid + case PB_AD_SLOT: + return imp.ext.data.pbAdSlot + case STORED_REQUEST: + return imp.ext.prebid.storedRequest.id + default: + return null } - if (GPID == impUnitCode) { - return imp.ext.gpid - } - if (PB_AD_SLOT == impUnitCode) { - return imp.ext.data.pbAdSlot - } - if (STORED_REQUEST == impUnitCode) { - return imp.ext.prebid.storedRequest.id - } - return null } protected static String getImpAdUnitCode(Imp imp) { - if (imp.ext.gpid) { - return imp.ext.gpid - } - if (imp.tagId) { - return imp.tagId - } - if (imp.ext.data.pbAdSlot) { - return imp.ext.data.pbAdSlot - } - if (imp.ext.prebid.storedRequest.id) { - return imp.ext.prebid.storedRequest.id - } - return null + [ + imp?.ext?.gpid, + imp?.tagId, + imp?.ext?.data?.pbAdSlot, + imp?.ext?.prebid?.storedRequest?.id + ].findResult { it } } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineContextSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineContextSpec.groovy index c957deffcb9..de900d10215 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineContextSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineContextSpec.groovy @@ -256,8 +256,7 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { def startTime = Instant.now() and: "Default bid request with multiplyB bidders" - def randomDomain = PBSUtils.randomString - def bidRequest = bidRequestClosure(randomDomain) + def bidRequest = bidRequestWithDomaint and: "Create account with rule engine config" def pbRuleEngine = createRulesEngineWithRule().tap { @@ -298,38 +297,25 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { assert getLogsByText(logs, INVALID_CONFIGURATION_FOR_STRINGS_LOG_WARNING(bidRequest.accountId, DOMAIN_IN)) where: - bidRequestClosure << [ - { String domain -> - getDefaultBidRequestWithMultiplyBidders(SITE).tap { - it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - } + bidRequestWithDomaint << [ + getDefaultBidRequestWithMultiplyBidders(SITE).tap { + it.site.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) }, - { String domain -> - getDefaultBidRequestWithMultiplyBidders(SITE).tap { - it.site.domain = domain - } + getDefaultBidRequestWithMultiplyBidders(SITE).tap { + it.site.domain = PBSUtils.randomString }, - { String domain -> - getDefaultBidRequestWithMultiplyBidders(APP).tap { - it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - } + getDefaultBidRequestWithMultiplyBidders(APP).tap { + it.app.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) }, - { String domain -> - getDefaultBidRequestWithMultiplyBidders(APP).tap { - it.app.domain = domain - } + getDefaultBidRequestWithMultiplyBidders(APP).tap { + it.app.domain = PBSUtils.randomString }, - { String domain -> - getDefaultBidRequestWithMultiplyBidders(DOOH).tap { - it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) - } + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + it.dooh.publisher = new Publisher(id: PBSUtils.randomString, domain: PBSUtils.randomString) }, - { String domain -> - getDefaultBidRequestWithMultiplyBidders(DOOH).tap { - it.dooh.domain = domain - } - } - ] + getDefaultBidRequestWithMultiplyBidders(DOOH).tap { + it.dooh.domain = PBSUtils.randomString + }] } def "PBS should exclude bidder when domainIn match with condition"() { @@ -457,7 +443,6 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { getDefaultBidRequestWithMultiplyBidders(DOOH).tap { it.dooh.domain = PBSUtils.randomString }] - } def "PBS should exclude bidder when bundle match with condition"() { @@ -672,7 +657,6 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } - def "PBS shouldn't exclude bidder when bundleIn not match with condition"() { given: "Default bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders(APP).tap { @@ -938,10 +922,9 @@ class RuleEngineContextSpec extends RuleEngineBaseSpec { null | null | PBSUtils.getRandomString() | null null | null | null | new PrebidStoredRequest(id: PBSUtils.getRandomString()) PBSUtils.getRandomString() | PBSUtils.getRandomString() | PBSUtils.getRandomString() | new PrebidStoredRequest(id: PBSUtils.getRandomString()) + null | PBSUtils.getRandomString() | PBSUtils.getRandomString() | new PrebidStoredRequest(id: PBSUtils.getRandomString()) + null | null | PBSUtils.getRandomString() | new PrebidStoredRequest(id: PBSUtils.getRandomString()) null | null | null | new PrebidStoredRequest(id: PBSUtils.getRandomString()) - null | null | PBSUtils.getRandomString() | null - null | PBSUtils.getRandomString() | null | null - PBSUtils.getRandomString() | null | null | null } def "PBS shouldn't exclude bidder when adUnitCode not match with condition"() { diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy index 68c718695de..4779f77af36 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEngineCoreSpec.groovy @@ -1,12 +1,10 @@ package org.prebid.server.functional.tests.module.pbruleengine -import org.prebid.server.functional.model.bidder.Openx import org.prebid.server.functional.model.config.RuleEngineModelDefault import org.prebid.server.functional.model.config.RuleEngineModelDefaultArgs import org.prebid.server.functional.model.config.RuleSet import org.prebid.server.functional.model.config.RulesEngineModelGroup import org.prebid.server.functional.model.config.Stage -import org.prebid.server.functional.model.request.auction.Amx import org.prebid.server.functional.model.request.auction.Imp import org.prebid.server.functional.util.PBSUtils @@ -80,11 +78,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { def "PBS should remove bidder from imps and use default 203 value for seatNonBid when seatNonBid null and exclude bidder in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(Imp.defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - } + it.imp.add(updateBidderImp(Imp.defaultImpression)) updateBidRequestWithGeoCountry(it) } @@ -118,7 +112,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS - and: "Analytics result detail info" + and: "Analytics result should contain detail info" def groups = pbRuleEngine.ruleSets[0].modelGroups[0] verifyAll(result.results[0]) { it.status == SUCCESS @@ -144,11 +138,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { def "PBS should remove bidder from imps and not update seatNonBid when returnAllBidStatus disabled and exclude bidder in account config"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(Imp.defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - } + it.imp.add(updateBidderImp(Imp.defaultImpression)) updateBidRequestWithGeoCountry(it) ext.prebid.tap { returnAllBidStatus = false @@ -189,7 +179,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS - and: "Analytics result detail info" + and: "Analytics result should contain detail info" def groups = pbRuleEngine.ruleSets[0].modelGroups[0] verifyAll(result.results[0]) { it.status == SUCCESS @@ -305,7 +295,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS - and: "Analytics result detail info" + and: "Analytics result should contain detail info" def groups = pbRuleEngine.ruleSets[0].modelGroups[0] verifyAll(result.results[0]) { it.status == SUCCESS @@ -365,7 +355,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS - and: "Analytics result detail info" + and: "Analytics result should contain detail info" def groups = pbRuleEngine.ruleSets[0].modelGroups[0] verifyAll(result.results[0]) { it.status == SUCCESS @@ -391,11 +381,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { def "PBS should leave only include bidder at imps when bidder include in account config"() { given: "Bid request with multiply imps bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(Imp.defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - } + it.imp.add(updateBidderImp(Imp.defaultImpression, [OPENX, AMX])) updateBidRequestWithGeoCountry(it) } @@ -428,7 +414,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS - and: "Analytics result detail info" + and: "Analytics result should contain detail info" def groups = pbRuleEngine.ruleSets[0].modelGroups[0] verifyAll(result.results[0]) { it.status == SUCCESS @@ -453,11 +439,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { def "PBS should only logATag when present only function log a tag"() { given: "Bid request with multiply imps bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { - it.imp.add(Imp.defaultImpression) - it.imp[1].ext.prebid.bidder.tap { - openx = Openx.defaultOpenx - amx = new Amx() - } + it.imp.add(updateBidderImp(Imp.defaultImpression, [OPENX, AMX])) updateBidRequestWithGeoCountry(it) } @@ -493,18 +475,23 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS - and: "Analytics result detail info" + and: "Analytics result should contain detail info" def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { + def impResult = result.results[0] + verifyAll(impResult) { it.status == SUCCESS it.values.analyticsKey == groups.analyticsKey it.values.modelVersion == groups.version it.values.analyticsValue == groups.rules.first.results.first.args.analyticsValue it.values.resultFunction == groups.rules.first.results.first.function.value it.values.conditionFired == groups.rules.first.conditions.first + + it.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + } + + verifyAll(impResult) { !it.values.biddersRemoved !it.values.seatNonBid - it.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] } } @@ -544,7 +531,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS - and: "Analytics result detail info" + and: "Analytics result should contain detail info" def groups = pbRuleEngine.ruleSets[1].modelGroups[0] verifyAll(result.results[0]) { it.status == SUCCESS @@ -608,7 +595,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS - and: "Analytics result detail info" + and: "Analytics result should contain detail info" def groups = pbRuleEngine.ruleSets[1].modelGroups[0] verifyAll(result.results[0]) { it.status == SUCCESS @@ -633,7 +620,6 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { stage << Stage.values() - PROCESSED_AUCTION_REQUEST } - def "PBS should take rule with higher weight and remove bidder when two model group with different weight"() { given: "Bid request with multiply bidders" def bidRequest = getDefaultBidRequestWithMultiplyBidders().tap { @@ -677,7 +663,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS - and: "Analytics result detail info" + and: "Analytics result should contain detail info" def groups = pbRuleEngine.ruleSets[0].modelGroups[1] verifyAll(result.results[0]) { it.status == SUCCESS @@ -737,7 +723,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS - and: "Analytics result detail info" + and: "Analytics result should contain detail info" def groups = pbRuleEngine.ruleSets[0].modelGroups[0] verifyAll(result.results[0]) { it.status == SUCCESS @@ -800,9 +786,10 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS - and: "Analytics result detail info" + and: "Analytics result should contain detail info" def groups = pbRuleEngine.ruleSets[0].modelGroups[0] - verifyAll(result.results[0]) { + def impResult = result.results[0] + verifyAll(impResult) { it.status == SUCCESS it.values.analyticsKey == groups.analyticsKey it.values.modelVersion == groups.version @@ -810,7 +797,10 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { it.values.resultFunction == LOG_A_TAG.value it.values.conditionFired == DEFAULT_CONDITIONS it.appliedTo.impIds == [APPLIED_FOR_ALL_IMPS] + } + and: "Analytics imp result shouldn't contain remove info" + verifyAll(impResult) { !it.values.biddersRemoved !it.values.seatNonBid } @@ -856,7 +846,7 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert result.name == PB_RULE_ENGINE.code assert result.status == SUCCESS - and: "Analytics result detail info" + and: "Analytics result should contain detail info" def groups = pbRuleEngine.ruleSets[0].modelGroups[0] verifyAll(result.results[0]) { it.status == SUCCESS @@ -877,6 +867,4 @@ class RuleEngineCoreSpec extends RuleEngineBaseSpec { assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id assert seatNonBid.nonBid[0].statusCode == REQUEST_BIDDER_REMOVED_BY_RULE_ENGINE_MODULE } - - } diff --git a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy index 2a145c7ac8d..3f63a04ec24 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/module/pbruleengine/RuleEnginePrivacySpec.groovy @@ -471,7 +471,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { }, getDefaultBidRequestWithMultiplyBidders(APP).tap { app.ext = new AppExt(data: new AppExtData(language: PBSUtils.randomString)) - }, + } ] } @@ -528,7 +528,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { }, getDefaultBidRequestWithMultiplyBidders(APP).tap { app.ext = new AppExt(data: null) - }, + } ] } @@ -903,7 +903,7 @@ class RuleEnginePrivacySpec extends RuleEngineBaseSpec { where: gdpr | condition - 0 | "TRUE" - 1 | "FALSE" + 0 | 'true' + 1 | 'false' } }