Skip to content

Commit 3f38263

Browse files
committed
refactor(sdk): update logs and added test cases for feature rollout whitelisting
1 parent ea0f749 commit 3f38263

File tree

6 files changed

+114
-21
lines changed

6 files changed

+114
-21
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88

99
### Changed
1010

11+
- Updated whitelisting logs for Feature Rollout campaign
12+
- Test cases added to verify whitelisting cases in Feature Rollout campaign
13+
14+
## [1.23.1] - 2020-10-21
15+
16+
### Changed
17+
1118
- Refactored code to prevent multiple calls to get data from User Storage Service, if used.
1219

1320
## [1.23.0] - 2021-09-29

tests/api/test_is_feature_enabled.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import json
1818
import random
1919
import mock
20+
import copy
2021

2122
import vwo
2223
from ..data.settings_files import SETTINGS_FILES
@@ -87,6 +88,52 @@ def test_is_feature_enabled_FR_W_100(self):
8788
for test in USER_EXPECTATIONS.get("T_100_W_10_20_30_40"):
8889
self.assertIs(self.vwo.is_feature_enabled("FR_T_100_W_100", test["user"]), test["variation"] is not None)
8990

91+
def test_is_feature_enabled_FR_T_100_WW(self):
92+
settings_file = copy.deepcopy(SETTINGS_FILES.get("FR_T_100_WW"))
93+
vwo_instance = vwo.launch(json.dumps(settings_file), is_development_mode=True, log_level=TEST_LOG_LEVEL)
94+
95+
false_variation_targeting_variables = {"safari": "true"}
96+
self.assertTrue(
97+
vwo_instance.is_feature_enabled(
98+
"FR_T_100_WW", "ashley", variation_targeting_variables=false_variation_targeting_variables
99+
)
100+
)
101+
102+
self.assertTrue(vwo_instance.is_feature_enabled("FR_T_100_WW", "ashley"))
103+
104+
def test_is_feature_enabled_FR_T_10_WW(self):
105+
settings_file = copy.deepcopy(SETTINGS_FILES.get("FR_T_100_WW"))
106+
settings_file["campaigns"][0]["percentTraffic"] = 10
107+
vwo_instance = vwo.launch(json.dumps(settings_file), is_development_mode=True, log_level=TEST_LOG_LEVEL)
108+
109+
false_variation_targeting_variables = {"safari": "true"}
110+
self.assertTrue(
111+
vwo_instance.is_feature_enabled(
112+
"FR_T_100_WW", "ashley", variation_targeting_variables=false_variation_targeting_variables
113+
)
114+
)
115+
116+
false_variation_targeting_variables = {"safari": "false"}
117+
self.assertFalse(
118+
vwo_instance.is_feature_enabled(
119+
"FR_T_100_WW", "ashley", variation_targeting_variables=false_variation_targeting_variables
120+
)
121+
)
122+
123+
def test_is_feature_enabled_FR_T_0_WW(self):
124+
settings_file = copy.deepcopy(SETTINGS_FILES.get("FR_T_100_WW"))
125+
settings_file["campaigns"][0]["percentTraffic"] = 0
126+
vwo_instance = vwo.launch(json.dumps(settings_file), is_development_mode=True, log_level=TEST_LOG_LEVEL)
127+
128+
false_variation_targeting_variables = {"safari": "true"}
129+
self.assertTrue(
130+
vwo_instance.is_feature_enabled(
131+
"FR_T_100_WW", "ashley", variation_targeting_variables=false_variation_targeting_variables
132+
)
133+
)
134+
135+
self.assertFalse(vwo_instance.is_feature_enabled("FR_T_100_WW", "ashley"))
136+
90137
def test_is_feature_enabled_FT_T_75_W_10_20_30_40(self):
91138
self.set_up("FT_T_75_W_10_20_30_40")
92139
feature_not_enabled_variations = ["Control"]

tests/data/settings_files.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,35 @@
348348
"accountId": 123456,
349349
"version": 2,
350350
},
351+
"FR_T_100_WW": {
352+
"sdkKey": "someuniquestuff1234567",
353+
"groups": {},
354+
"campaignGroups": {},
355+
"campaigns": [
356+
{
357+
"goals": [],
358+
"variations": [
359+
{
360+
"id": "1",
361+
"name": "Control",
362+
"weight": 100,
363+
"segments": {"or": [{"custom_variable": {"safari": "true"}}]},
364+
}
365+
],
366+
"variables": [{"id": 2, "key": "BOOLEAN_VARIABLE", "type": "boolean", "value": True}],
367+
"id": 29,
368+
"percentTraffic": 100,
369+
"isForcedVariationEnabled": True,
370+
"key": "FR_T_100_WW",
371+
"name": "Campaign-24",
372+
"status": "RUNNING",
373+
"type": "FEATURE_ROLLOUT",
374+
"segments": {},
375+
}
376+
],
377+
"accountId": 123456,
378+
"version": 2,
379+
},
351380
"FR_WRONG_VARIABLE_TYPE": {
352381
"sdkKey": "someuniquestuff1234567",
353382
"campaigns": [

vwo/constants/constants.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ class PUSH_API:
106106
PY_VARIABLE_TYPES = {"string": str, "integer": int, "double": float, "boolean": bool, "json": dict}
107107

108108

109+
class SEGMENTATION_TYPES:
110+
WHITELISTING = "whitelisting"
111+
PRE_SEGMENTATION = "pre_segmentation"
112+
113+
109114
# For exposing log levels to vwo_instance
110115
class LOG_LEVELS:
111116
DEBUG = logging.DEBUG

vwo/core/variation_decider.py

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -133,16 +133,6 @@ def get_variation(self, user_id, campaign, **kwargs): # noqa: C901
133133
# Evaluate whitelisting at first
134134
targeted_variation = self.find_targeted_variation(user_id, campaign, variation_targeting_variables)
135135
if targeted_variation:
136-
self.logger.log(
137-
LogLevelEnum.INFO,
138-
LogMessageEnum.INFO_MESSAGES.GOT_VARIATION_FOR_USER.format(
139-
file=FILE,
140-
variation_name=targeted_variation.get("name"),
141-
user_id=user_id,
142-
campaign_key=campaign.get("key"),
143-
campaign_type=campaign.get("type"),
144-
),
145-
)
146136

147137
decision.update({"from_user_storage_service": False, "is_user_whitelisted": True})
148138
if campaign.get("type") == constants.CAMPAIGN_TYPES.FEATURE_ROLLOUT:
@@ -435,18 +425,23 @@ def find_targeted_variation(self, user_id, campaign, variation_targeting_variabl
435425
constants.MAX_TRAFFIC_VALUE,
436426
)
437427
targeted_variation = self.bucketer.get_allocated_item(white_listed_variations_list, bucket_value)
438-
variation_status = "and variation {variation_name} is assigned"
428+
variation_status = (
429+
"and variation {variation_name} is assigned"
430+
if campaign.get("type") != constants.CAMPAIGN_TYPES.FEATURE_ROLLOUT
431+
else ""
432+
)
439433
self.logger.log(
440434
LogLevelEnum.INFO,
441435
LogMessageEnum.INFO_MESSAGES.SEGMENTATION_STATUS.format(
442436
file=FILE,
443437
campaign_key=campaign.get("key"),
444438
user_id=user_id,
439+
campaign_type=campaign.get("type"),
445440
variables=variation_targeting_variables,
446441
variation_status=variation_status.format(
447442
variation_name=targeted_variation.get("name") if targeted_variation else None
448443
),
449-
segmentation_type="whitelisting",
444+
segmentation_type=constants.SEGMENTATION_TYPES.WHITELISTING,
450445
status=ResultStatus.PASSED if targeted_variation is not None else ResultStatus.FAILED,
451446
),
452447
disable_logs,
@@ -488,7 +483,7 @@ def evaluate_pre_segmentation(self, user_id, campaign, custom_variables, disable
488483
file=FILE,
489484
user_id=user_id,
490485
campaign_key=campaign.get("key"),
491-
segmentation_type="pre_segmentation",
486+
segmentation_type=constants.SEGMENTATION_TYPES.PRE_SEGMENTATION,
492487
),
493488
disable_logs,
494489
)
@@ -501,9 +496,10 @@ def evaluate_pre_segmentation(self, user_id, campaign, custom_variables, disable
501496
file=FILE,
502497
user_id=user_id,
503498
campaign_key=campaign.get("key"),
499+
campaign_type=campaign.get("type"),
504500
variables=custom_variables,
505501
variation_status="",
506-
segmentation_type="pre_segmentation",
502+
segmentation_type=constants.SEGMENTATION_TYPES.PRE_SEGMENTATION,
507503
status=ResultStatus.PASSED if result else ResultStatus.FAILED,
508504
),
509505
disable_logs,
@@ -571,7 +567,10 @@ def _get_white_listed_variations_list(self, user_id, campaign, variation_targeti
571567
self.logger.log(
572568
LogLevelEnum.DEBUG,
573569
LogMessageEnum.DEBUG_MESSAGES.NO_VARIABLES.format(
574-
file=FILE, user_id=user_id, campaign_key=campaign.get("key"), segmentation_type="whitelisting"
570+
file=FILE,
571+
user_id=user_id,
572+
campaign_key=campaign.get("key"),
573+
segmentation_type=constants.SEGMENTATION_TYPES.WHITELISTING,
575574
),
576575
disable_logs,
577576
)
@@ -590,7 +589,9 @@ def _get_white_listed_variations_list(self, user_id, campaign, variation_targeti
590589
user_id=user_id,
591590
variation_name=variation.get("name"),
592591
campaign_key=campaign.get("key"),
593-
variation_status="for variation %s" % variation.get("name"),
592+
variation_status="for variation %s" % variation.get("name")
593+
if campaign.get("type") != constants.CAMPAIGN_TYPES.FEATURE_ROLLOUT
594+
else "",
594595
),
595596
disable_logs,
596597
)
@@ -604,9 +605,11 @@ def _get_white_listed_variations_list(self, user_id, campaign, variation_targeti
604605
user_id=user_id,
605606
status="passed" if result else "failed",
606607
variables=variation_targeting_variables,
607-
variation_status="for variation %s" % variation.get("name"),
608+
variation_status="for variation %s" % variation.get("name")
609+
if campaign.get("type") != constants.CAMPAIGN_TYPES.FEATURE_ROLLOUT
610+
else "and becomes part of the rollout",
608611
campaign_key=campaign.get("key"),
609-
segmentation_type="white_listing",
612+
segmentation_type=constants.SEGMENTATION_TYPES.WHITELISTING,
610613
),
611614
disable_logs,
612615
)
@@ -740,10 +743,12 @@ def _get_bucketed_variation(self, user_id, campaign, decision, goal_data):
740743
LogLevelEnum.INFO,
741744
LogMessageEnum.INFO_MESSAGES.GOT_VARIATION_FOR_USER.format(
742745
file=FILE,
743-
variation_name=variation.get("name"),
744746
user_id=user_id,
745747
campaign_key=campaign.get("key"),
746748
campaign_type=campaign.get("type"),
749+
variation_status="got variation_name:%s" % variation.get("name")
750+
if campaign.get("type") != constants.CAMPAIGN_TYPES.FEATURE_ROLLOUT
751+
else "becomes part of rollout",
747752
),
748753
)
749754

vwo/enums/log_message_enum.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class INFO_MESSAGES:
6767
GOT_STORED_VARIATION = "({file}): [API_NAME] Got stored variation:{variation_name} of campaign_key:{campaign_key} for user_id:{user_id} from UserStorage"
6868
NO_VARIATION_ALLOCATED = "({file}): [API_NAME] user_id:{user_id} of campaign_key:{campaign_key} type: {campaign_type} did not get any variation"
6969
USER_ELIGIBILITY_FOR_CAMPAIGN = "({file}): [API_NAME] Is user_id:{user_id} part of campaign? {is_user_part}"
70-
GOT_VARIATION_FOR_USER = "({file}): [API_NAME] user_id:{user_id} for campaign_key:{campaign_key} type: {campaign_type} got variation_name:{variation_name}"
70+
GOT_VARIATION_FOR_USER = "({file}): [API_NAME] user_id:{user_id} for campaign_key:{campaign_key} type: {campaign_type} {variation_status}"
7171
USER_GOT_NO_VARIATION = "({file}): [API_NAME] user_id:{user_id} for campaign_key:{campaign_key} type: {campaign_type} did not allot any variation"
7272
IMPRESSION_SUCCESS = "({file}): [API_NAME] Impression event - {end_point} was successfully received by VWO"
7373
IMPRESSION_SUCCESS_QUEUE = "({file}): [API_NAME] Impression event - {end_point} was successfully pushed in queue, current queue length : {queue_length}, queue summary: {queue_metadata}"
@@ -89,7 +89,7 @@ class INFO_MESSAGES:
8989

9090
VARIABLE_FOUND = "({file}): [API_NAME] Value for variable:{variable_key} of campaign_key:{campaign_key} and campaign type: {campaign_type} is:{variable_value} for user:{user_id}"
9191

92-
SEGMENTATION_STATUS = "({file}): [API_NAME] user_id:{user_id} of campaign_key:{campaign_key} with variables: {variables} {status} {segmentation_type} {variation_status}"
92+
SEGMENTATION_STATUS = "({file}): [API_NAME] user_id:{user_id} of campaign_key:{campaign_key} type: {campaign_type} with variables: {variables} {status} {segmentation_type} {variation_status}"
9393

9494
GOAL_ALREADY_TRACKED = "({file}): [API_NAME] Goal:{goal_identifier} of Campaign:{campaign_key} for User ID:{user_id} has already been tracked earlier. Skipping now."
9595
USER_ALREADY_TRACKED = "({file}): [API_NAME] User ID:{user_id} for Campaign:{campaign_key} has already been tracked earlier for '{api_method}' API. Skipping now"

0 commit comments

Comments
 (0)