fix: decision service for group and multiple experiments.#322
fix: decision service for group and multiple experiments.#322jaeopt merged 16 commits intomasteroptimizely/python-sdk:masterfrom uzair/decision-service-for-group-fixesoptimizely/python-sdk:uzair/decision-service-for-group-fixesCopy head branch name to clipboard
Conversation
jaeopt
left a comment
There was a problem hiding this comment.
LGTM with a comment fix suggested
optimizely/decision_service.py
Outdated
| decide_reasons += reasons | ||
| if experiment and experiment.id in feature.experimentIds: | ||
|
|
||
| # Next check if the feature is being experimented on |
|
Remove the code in project_config.py where |
msohailhussain
left a comment
There was a problem hiding this comment.
I am not convinced on unit test changes, doesn't make sense to mock the value and expect the same value. Either we should mock Bucketer or remove these unit tests.
tests/test_decision_service.py
Outdated
| def test_get_variation_for_feature__returns_none_for_user_not_in_group(self): | ||
| """ Test that get_variation_for_feature returns None for | ||
| user not in group and the feature is not part of a rollout. """ | ||
| user not in group and the feature is not part of a rollout. """ |
There was a problem hiding this comment.
indent like before.
| side_effect=[[False, []], [True, []]], | ||
| ) as mock_audience_check, self.mock_decision_logger as mock_decision_service_logging, mock.patch( | ||
| "optimizely.bucketer.Bucketer.bucket", return_value=[expected_variation, []]): | ||
|
|
There was a problem hiding this comment.
don't remove existing line.
tests/test_decision_service.py
Outdated
| ) | ||
| mock_decision_service_logging.error.assert_called_once_with( | ||
| enums.Errors.INVALID_GROUP_ID.format("_get_variation_for_feature") | ||
| variation_received, _ = self.decision_service.get_variation_for_feature( |
There was a problem hiding this comment.
Please add logging.
jaeopt
left a comment
There was a problem hiding this comment.
Those test cases updated all turn out to be same - "returns rollout decisions when feature tests cannot find variations".
Can we fix them (and add more tests) to test
- group exclusion in a feature
- multiple non-grouped tests in a feature
tests/test_decision_service.py
Outdated
| ) as mock_get_experiment_in_group, mock.patch( | ||
| "optimizely.decision_service.DecisionService.get_variation" | ||
| ) as mock_decision: | ||
| "optimizely.decision_service.DecisionService.get_variation", |
There was a problem hiding this comment.
This mock does not give a test for "group". It tests the cases for feature-tests not bucketed.
Can we mock bucket to control group-exclusion cases?
| with mock.patch( | ||
| "optimizely.decision_service.DecisionService.get_experiment_in_group", | ||
| return_value=(self.project_config.get_experiment_from_key("group_exp_1"), []), | ||
| ) as mock_get_experiment_in_group, mock.patch( | ||
| "optimizely.decision_service.DecisionService.get_variation", | ||
| return_value=(expected_variation, []), | ||
| ) as mock_decision: |
There was a problem hiding this comment.
This mock does not give a test for "group". It tests the cases for feature-tests not bucketed.
Can we mock bucket to control group-exclusion cases?
tests/test_decision_service.py
Outdated
| feature = self.project_config.get_feature_from_key("test_feature_in_group") | ||
| feature.groupId = "aabbccdd" |
There was a problem hiding this comment.
We do not use this feature "groupId" any more. This test is not valid any more.
| with mock.patch( | ||
| "optimizely.decision_service.DecisionService.get_experiment_in_group", | ||
| return_value=[self.project_config.get_experiment_from_key("group_exp_2"), []], | ||
| "optimizely.decision_service.DecisionService.get_variation", | ||
| return_value=[None, []], | ||
| ) as mock_decision: |
tests/base.py
Outdated
| 'forcedVariations': {}, | ||
| 'trafficAllocation': [ | ||
| {'entityId': '', 'endOfRange': 2500}, | ||
| {'entityId': '', 'endOfRange': 5000}, |
There was a problem hiding this comment.
can we generate datafile like this?
| 'forcedVariations': {}, | ||
| 'trafficAllocation': [ | ||
| {'entityId': '222239', 'endOfRange': 2500}, | ||
| {'entityId': '', 'endOfRange': 5000}, |
There was a problem hiding this comment.
need to discuss offline.
| {'number_of_projects': entities.Variable('131', 'number_of_projects', 'integer', '10')}, | ||
| ), | ||
| 'test_feature_in_group': entities.FeatureFlag('91113', 'test_feature_in_group', ['32222'], '', {}, '19228'), | ||
| 'test_feature_in_group': entities.FeatureFlag('91113', 'test_feature_in_group', ['32222'], '', {}), |
| def test_get_variation_for_feature__returns_none_for_user_in_group_experiment_not_associated_with_feature( | ||
| self, | ||
| ): | ||
| """ Test that if a user is in the mutex group but the experiment is |
There was a problem hiding this comment.
Need to revise description, doesn't make sense. let's talk offline.
| "test_user", | ||
| None, | ||
| False | ||
| self.project_config, self.project_config.get_experiment_from_id("32222"), "test_user", None, False |
There was a problem hiding this comment.
Why we are asserting get_experiment_from_id.
| 'audienceIds': ['11160'], | ||
| 'layerId': '211183', | ||
| 'variations': [ | ||
| {'key': 'var_1', 'id': '38901'}, |
There was a problem hiding this comment.
append experiment key name with the key.
| 'audienceIds': ['11160'], | ||
| 'layerId': '211184', | ||
| 'variations': [ | ||
| {'key': 'var_1', 'id': '38905'} |
| 'audienceIds': ['11160'], | ||
| 'layerId': '211185', | ||
| 'variations': [ | ||
| {'key': 'var_1', 'id': '38906'} |
jaeopt
left a comment
There was a problem hiding this comment.
The new tests look great.
I suggest a few small changes and add one more test.
| ) | ||
|
|
||
| mock_config_logging.debug.assert_called_with('Assigned bucket 2400 to user with bucketing ID "test_user".') | ||
| mock_generate_bucket_value.assert_called_with('test_user42222') |
There was a problem hiding this comment.
| mock_generate_bucket_value.assert_called_with('test_user42222') | |
| mock_generate_bucket_value.assert_called_with('test_user19229') |
group bucketing is more interesting here
| variation_received, | ||
| ) | ||
| mock_config_logging.debug.assert_called_with('Assigned bucket 4000 to user with bucketing ID "test_user".') | ||
| mock_generate_bucket_value.assert_called_with('test_user42223') |
| variation_received, | ||
| ) | ||
| mock_config_logging.debug.assert_called_with('Assigned bucket 6500 to user with bucketing ID "test_user".') | ||
| mock_generate_bucket_value.assert_called_with('test_user42224') |
tests/test_decision_service.py
Outdated
| self, | ||
| ): | ||
| """ Test that if a user is in the mutex group and the user bucket value should be equal to 2500 | ||
| or greater than 5000.""" |
There was a problem hiding this comment.
| or greater than 5000.""" | |
| or less than 5000.""" |
tests/test_decision_service.py
Outdated
| self, | ||
| ): | ||
| """ Test that if a user is in the mutex group and the user bucket value should be equal to 5000 | ||
| or greater than 7500.""" |
There was a problem hiding this comment.
| or greater than 7500.""" | |
| or less than 7500.""" |
| variation_received, | ||
| ) | ||
|
|
||
| mock_generate_bucket_value.assert_called_with('test_user211147') |
There was a problem hiding this comment.
| mock_generate_bucket_value.assert_called_with('test_user211147') | |
| mock_generate_bucket_value.assert_called_with('test_user19229') |
tests/test_decision_service.py
Outdated
| self, | ||
| ): | ||
| """ Test that if a user is in the non-mutex group and the user bucket value should be equal to 2500 | ||
| or greater than 5000.""" |
There was a problem hiding this comment.
| or greater than 5000.""" | |
| or less than 5000.""" |
tests/test_decision_service.py
Outdated
| self, | ||
| ): | ||
| """ Test that if a user is in the non-mutex group and the user bucket value should be equal to 5000 | ||
| or greater than 7500.""" |
There was a problem hiding this comment.
| or greater than 7500.""" | |
| or less than 7500.""" |
| ) | ||
|
|
||
| mock_generate_bucket_value.assert_called_with('test_user211147') | ||
| mock_config_logging.debug.assert_called_with('Assigned bucket 8000 to user with bucketing ID "test_user".') |
There was a problem hiding this comment.
These tests look great.
One missing test is that in multiple experiments, one of the test can be set to missing target by audience (instead of traffic allocation). We do not need to repeat multiple traffic allocation tests. One of them can be changed to use audience-mismatch.
Summary
get_variation_for_featurecode updated to fix group and multiple experiment issues.Fixes:
feature.experimentIds[0]. Which is fixed by using loop.Test plan
Issues
get_variation_for_featureafter current changes in Python.