Skip to content

Commit 44ac6c7

Browse files
rashidspaliabbasrizvi
authored andcommitted
feat: Add decision listener for activate - get_variation (#167)
1 parent f772ecc commit 44ac6c7

File tree

3 files changed

+136
-23
lines changed

3 files changed

+136
-23
lines changed

Diff for: optimizely/helpers/enums.py

+7
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ class NotificationTypes(object):
8989
Experiment experiment, str user_id, dict attributes (can be None), Variation variation, Event event
9090
TRACK notification listener has the following parameters:
9191
str event_key, str user_id, dict attributes (can be None), event_tags (can be None), Event event
92+
DECISION notification listener has the following parameters:
93+
DecisionInfoTypes type, str user_id, dict attributes (can be None), dict decision_info
9294
"""
9395
ACTIVATE = "ACTIVATE:experiment, user_id, attributes, variation, event"
9496
TRACK = "TRACK:event_key, user_id, attributes, event_tags, event"
97+
DECISION = "DECISON:type, user_id, attributes, decision_info"
98+
99+
100+
class DecisionInfoTypes(object):
101+
EXPERIMENT = "experiment"

Diff for: optimizely/optimizely.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ def get_variation(self, experiment_key, user_id, attributes=None):
336336
return None
337337

338338
experiment = self.config.get_experiment_from_key(experiment_key)
339+
variation_key = None
339340

340341
if not experiment:
341342
self.logger.info('Experiment key "%s" is invalid. Not activating user "%s".' % (
@@ -349,9 +350,20 @@ def get_variation(self, experiment_key, user_id, attributes=None):
349350

350351
variation = self.decision_service.get_variation(experiment, user_id, attributes)
351352
if variation:
352-
return variation.key
353-
354-
return None
353+
variation_key = variation.key
354+
355+
self.notification_center.send_notifications(
356+
enums.NotificationTypes.DECISION,
357+
enums.DecisionInfoTypes.EXPERIMENT,
358+
user_id,
359+
attributes or {},
360+
{
361+
'experiment_key': experiment_key,
362+
'variation_key': variation_key
363+
}
364+
)
365+
366+
return variation_key
355367

356368
def is_feature_enabled(self, feature_key, user_id, attributes=None):
357369
""" Returns true if the feature is enabled for the given user.

Diff for: tests/test_optimizely.py

+114-20
Original file line numberDiff line numberDiff line change
@@ -398,42 +398,92 @@ def test_remove_listener(self):
398398
self.assertEqual(0, len(self.optimizely.notification_center.notifications[enums.NotificationTypes.TRACK]))
399399
self.assertEqual(0, len(self.optimizely.notification_center.notifications[enums.NotificationTypes.ACTIVATE]))
400400

401-
def test_activate_listener(self):
402-
""" Test that activate calls broadcast activate with proper parameters. """
401+
def test_activate_and_decision_listener(self):
402+
""" Test that activate calls broadcast activate and decision with proper parameters. """
403403

404404
with mock.patch(
405405
'optimizely.decision_service.DecisionService.get_variation',
406406
return_value=self.project_config.get_variation_from_id('test_experiment', '111129')), \
407407
mock.patch('optimizely.event_dispatcher.EventDispatcher.dispatch_event') as mock_dispatch, \
408-
mock.patch('optimizely.notification_center.NotificationCenter.send_notifications') as mock_broadcast_activate:
408+
mock.patch('optimizely.notification_center.NotificationCenter.send_notifications') as mock_broadcast:
409409
self.assertEqual('variation', self.optimizely.activate('test_experiment', 'test_user'))
410410

411-
mock_broadcast_activate.assert_called_once_with(enums.NotificationTypes.ACTIVATE,
412-
self.project_config.get_experiment_from_key('test_experiment'),
413-
'test_user', None,
414-
self.project_config.get_variation_from_id('test_experiment',
415-
'111129'),
416-
mock_dispatch.call_args[0][0])
411+
self.assertEqual(mock_broadcast.call_count, 2)
417412

418-
def test_activate_listener_with_attr(self):
419-
""" Test that activate calls broadcast activate with proper parameters. """
413+
mock_broadcast.assert_has_calls([
414+
mock.call(
415+
enums.NotificationTypes.DECISION,
416+
'experiment',
417+
'test_user',
418+
{},
419+
{
420+
'experiment_key': 'test_experiment',
421+
'variation_key': 'variation'
422+
}
423+
),
424+
mock.call(
425+
enums.NotificationTypes.ACTIVATE,
426+
self.project_config.get_experiment_from_key('test_experiment'),
427+
'test_user', None,
428+
self.project_config.get_variation_from_id('test_experiment', '111129'),
429+
mock_dispatch.call_args[0][0]
430+
)
431+
])
432+
433+
def test_activate_and_decision_listener_with_attr(self):
434+
""" Test that activate calls broadcast activate and decision with proper parameters. """
420435

421436
with mock.patch(
422437
'optimizely.decision_service.DecisionService.get_variation',
423438
return_value=self.project_config.get_variation_from_id('test_experiment', '111129')), \
424439
mock.patch('optimizely.event_dispatcher.EventDispatcher.dispatch_event') as mock_dispatch, \
425-
mock.patch('optimizely.notification_center.NotificationCenter.send_notifications') as mock_broadcast_activate:
440+
mock.patch('optimizely.notification_center.NotificationCenter.send_notifications') as mock_broadcast:
426441
self.assertEqual('variation',
427442
self.optimizely.activate('test_experiment', 'test_user', {'test_attribute': 'test_value'}))
428443

429-
mock_broadcast_activate.assert_called_once_with(enums.NotificationTypes.ACTIVATE,
430-
self.project_config.get_experiment_from_key('test_experiment'),
431-
'test_user', {'test_attribute': 'test_value'},
432-
self.project_config.get_variation_from_id(
433-
'test_experiment', '111129'
434-
),
435-
mock_dispatch.call_args[0][0]
436-
)
444+
self.assertEqual(mock_broadcast.call_count, 2)
445+
446+
mock_broadcast.assert_has_calls([
447+
mock.call(
448+
enums.NotificationTypes.DECISION,
449+
'experiment',
450+
'test_user',
451+
{'test_attribute': 'test_value'},
452+
{
453+
'experiment_key': 'test_experiment',
454+
'variation_key': 'variation'
455+
}
456+
),
457+
mock.call(
458+
enums.NotificationTypes.ACTIVATE,
459+
self.project_config.get_experiment_from_key('test_experiment'),
460+
'test_user', {'test_attribute': 'test_value'},
461+
self.project_config.get_variation_from_id('test_experiment', '111129'),
462+
mock_dispatch.call_args[0][0]
463+
)
464+
])
465+
466+
def test_decision_listener__user_not_in_experiment(self):
467+
""" Test that activate calls broadcast decision with variation_key 'None' \
468+
when user not in experiment. """
469+
470+
with mock.patch(
471+
'optimizely.decision_service.DecisionService.get_variation',
472+
return_value=None), \
473+
mock.patch('optimizely.event_dispatcher.EventDispatcher.dispatch_event'), \
474+
mock.patch('optimizely.notification_center.NotificationCenter.send_notifications') as mock_broadcast_decision:
475+
self.assertEqual(None, self.optimizely.activate('test_experiment', 'test_user'))
476+
477+
mock_broadcast_decision.assert_called_once_with(
478+
enums.NotificationTypes.DECISION,
479+
'experiment',
480+
'test_user',
481+
{},
482+
{
483+
'experiment_key': 'test_experiment',
484+
'variation_key': None
485+
}
486+
)
437487

438488
def test_track_listener(self):
439489
""" Test that track calls notification broadcaster. """
@@ -1395,6 +1445,50 @@ def test_track__invalid_user_id(self):
13951445
self.assertIsNone(self.optimizely.track('test_event', 99))
13961446
mock_client_logging.error.assert_called_once_with('Provided "user_id" is in an invalid format.')
13971447

1448+
def test_get_variation(self):
1449+
""" Test that get_variation returns valid variation and broadcasts decision with proper parameters. """
1450+
1451+
with mock.patch(
1452+
'optimizely.decision_service.DecisionService.get_variation',
1453+
return_value=self.project_config.get_variation_from_id('test_experiment', '111129')), \
1454+
mock.patch('optimizely.notification_center.NotificationCenter.send_notifications') as mock_broadcast:
1455+
self.assertEqual('variation', self.optimizely.get_variation('test_experiment', 'test_user'))
1456+
1457+
self.assertEqual(mock_broadcast.call_count, 1)
1458+
1459+
mock_broadcast.assert_called_once_with(
1460+
enums.NotificationTypes.DECISION,
1461+
'experiment',
1462+
'test_user',
1463+
{},
1464+
{
1465+
'experiment_key': 'test_experiment',
1466+
'variation_key': 'variation'
1467+
}
1468+
)
1469+
1470+
def test_get_variation__returns_none(self):
1471+
""" Test that get_variation returns no variation and broadcasts decision with proper parameters. """
1472+
1473+
with mock.patch(
1474+
'optimizely.decision_service.DecisionService.get_variation', return_value=None), \
1475+
mock.patch('optimizely.notification_center.NotificationCenter.send_notifications') as mock_broadcast:
1476+
self.assertEqual(None, self.optimizely.get_variation('test_experiment', 'test_user',
1477+
attributes={'test_attribute': 'test_value'}))
1478+
1479+
self.assertEqual(mock_broadcast.call_count, 1)
1480+
1481+
mock_broadcast.assert_called_once_with(
1482+
enums.NotificationTypes.DECISION,
1483+
'experiment',
1484+
'test_user',
1485+
{'test_attribute': 'test_value'},
1486+
{
1487+
'experiment_key': 'test_experiment',
1488+
'variation_key': None
1489+
}
1490+
)
1491+
13981492
def test_get_variation__invalid_object(self):
13991493
""" Test that get_variation logs error if Optimizely object is not created correctly. """
14001494

0 commit comments

Comments
 (0)