|
1 | | -# Copyright 2016-2019, Optimizely |
| 1 | +# Copyright 2016-2019, 2026, Optimizely |
2 | 2 | # Licensed under the Apache License, Version 2.0 (the "License"); |
3 | 3 | # you may not use this file except in compliance with the License. |
4 | 4 | # You may obtain a copy of the License at |
|
11 | 11 | # See the License for the specific language governing permissions and |
12 | 12 | # limitations under the License. |
13 | 13 |
|
| 14 | +import copy |
14 | 15 | from unittest import mock |
15 | 16 | import unittest |
16 | 17 | from operator import itemgetter |
@@ -1015,3 +1016,123 @@ def test_create_conversion_event__when_event_is_used_in_multiple_experiments(sel |
1015 | 1016 | event_builder.EventBuilder.HTTP_VERB, |
1016 | 1017 | event_builder.EventBuilder.HTTP_HEADERS, |
1017 | 1018 | ) |
| 1019 | + |
| 1020 | + |
| 1021 | +class ImpressionEventIdNormalizationTest(base.BaseTest): |
| 1022 | + """Impression-event normalization rules for campaign_id, variation_id, and entity_id.""" |
| 1023 | + |
| 1024 | + def setUp(self, *args, **kwargs): |
| 1025 | + base.BaseTest.setUp(self, 'config_dict_with_multiple_experiments') |
| 1026 | + self.event_builder = self.optimizely.event_builder |
| 1027 | + self.experiment = self.project_config.get_experiment_from_key('test_experiment') |
| 1028 | + |
| 1029 | + def _build_impression(self, experiment, variation_id): |
| 1030 | + return self.event_builder._get_required_params_for_impression(experiment, variation_id) |
| 1031 | + |
| 1032 | + def _with_layer_id(self, layer_id): |
| 1033 | + experiment = copy.deepcopy(self.experiment) |
| 1034 | + experiment.layerId = layer_id |
| 1035 | + return experiment |
| 1036 | + |
| 1037 | + def _decision(self, snapshot): |
| 1038 | + return snapshot[event_builder.EventBuilder.EventParams.DECISIONS][0] |
| 1039 | + |
| 1040 | + def _event(self, snapshot): |
| 1041 | + return snapshot[event_builder.EventBuilder.EventParams.EVENTS][0] |
| 1042 | + |
| 1043 | + # campaign_id normalization (US1) ---------------------------------------------- |
| 1044 | + |
| 1045 | + def test_campaign_id_valid_numeric_layer_id_passes_through(self): |
| 1046 | + experiment = self._with_layer_id('111182') |
| 1047 | + snapshot = self._build_impression(experiment, '111129') |
| 1048 | + self.assertEqual(self._decision(snapshot)['campaign_id'], '111182') |
| 1049 | + |
| 1050 | + def test_campaign_id_empty_string_falls_back_to_experiment_id(self): |
| 1051 | + experiment = self._with_layer_id('') |
| 1052 | + snapshot = self._build_impression(experiment, '111129') |
| 1053 | + self.assertEqual(self._decision(snapshot)['campaign_id'], experiment.id) |
| 1054 | + |
| 1055 | + def test_campaign_id_none_falls_back_to_experiment_id(self): |
| 1056 | + experiment = self._with_layer_id(None) |
| 1057 | + snapshot = self._build_impression(experiment, '111129') |
| 1058 | + self.assertEqual(self._decision(snapshot)['campaign_id'], experiment.id) |
| 1059 | + |
| 1060 | + def test_campaign_id_non_numeric_string_falls_back_to_experiment_id(self): |
| 1061 | + experiment = self._with_layer_id('abc') |
| 1062 | + snapshot = self._build_impression(experiment, '111129') |
| 1063 | + self.assertEqual(self._decision(snapshot)['campaign_id'], experiment.id) |
| 1064 | + |
| 1065 | + def test_campaign_id_whitespace_falls_back_to_experiment_id(self): |
| 1066 | + experiment = self._with_layer_id(' ') |
| 1067 | + snapshot = self._build_impression(experiment, '111129') |
| 1068 | + self.assertEqual(self._decision(snapshot)['campaign_id'], experiment.id) |
| 1069 | + |
| 1070 | + def test_campaign_id_integer_value_falls_back_to_experiment_id(self): |
| 1071 | + experiment = self._with_layer_id(111182) |
| 1072 | + snapshot = self._build_impression(experiment, '111129') |
| 1073 | + self.assertEqual(self._decision(snapshot)['campaign_id'], experiment.id) |
| 1074 | + |
| 1075 | + # variation_id normalization (US2) --------------------------------------------- |
| 1076 | + |
| 1077 | + def test_variation_id_valid_numeric_passes_through(self): |
| 1078 | + snapshot = self._build_impression(self.experiment, '111129') |
| 1079 | + self.assertEqual(self._decision(snapshot)['variation_id'], '111129') |
| 1080 | + |
| 1081 | + def test_variation_id_empty_string_becomes_none(self): |
| 1082 | + snapshot = self._build_impression(self.experiment, '') |
| 1083 | + self.assertIsNone(self._decision(snapshot)['variation_id']) |
| 1084 | + |
| 1085 | + def test_variation_id_non_numeric_string_becomes_none(self): |
| 1086 | + snapshot = self._build_impression(self.experiment, 'variation_a') |
| 1087 | + self.assertIsNone(self._decision(snapshot)['variation_id']) |
| 1088 | + |
| 1089 | + def test_variation_id_none_stays_none(self): |
| 1090 | + snapshot = self._build_impression(self.experiment, None) |
| 1091 | + self.assertIsNone(self._decision(snapshot)['variation_id']) |
| 1092 | + |
| 1093 | + # entity_id normalization (US3) and US3 acceptance #5: byte-equality with campaign_id |
| 1094 | + |
| 1095 | + def test_entity_id_valid_layer_id_passes_through(self): |
| 1096 | + experiment = self._with_layer_id('111182') |
| 1097 | + snapshot = self._build_impression(experiment, '111129') |
| 1098 | + self.assertEqual(self._event(snapshot)['entity_id'], '111182') |
| 1099 | + |
| 1100 | + def test_entity_id_empty_falls_back_to_experiment_id(self): |
| 1101 | + experiment = self._with_layer_id('') |
| 1102 | + snapshot = self._build_impression(experiment, '111129') |
| 1103 | + self.assertEqual(self._event(snapshot)['entity_id'], experiment.id) |
| 1104 | + |
| 1105 | + def test_entity_id_non_numeric_falls_back_to_experiment_id(self): |
| 1106 | + experiment = self._with_layer_id('abc') |
| 1107 | + snapshot = self._build_impression(experiment, '111129') |
| 1108 | + self.assertEqual(self._event(snapshot)['entity_id'], experiment.id) |
| 1109 | + |
| 1110 | + def test_entity_id_equals_campaign_id_when_layer_invalid(self): |
| 1111 | + experiment = self._with_layer_id('') |
| 1112 | + snapshot = self._build_impression(experiment, '111129') |
| 1113 | + self.assertEqual( |
| 1114 | + self._event(snapshot)['entity_id'], |
| 1115 | + self._decision(snapshot)['campaign_id'], |
| 1116 | + ) |
| 1117 | + |
| 1118 | + def test_entity_id_equals_campaign_id_when_layer_valid(self): |
| 1119 | + experiment = self._with_layer_id('111182') |
| 1120 | + snapshot = self._build_impression(experiment, '111129') |
| 1121 | + self.assertEqual( |
| 1122 | + self._event(snapshot)['entity_id'], |
| 1123 | + self._decision(snapshot)['campaign_id'], |
| 1124 | + ) |
| 1125 | + |
| 1126 | + # Negative regression: conversion events are out of scope (FR-010). |
| 1127 | + |
| 1128 | + def test_conversion_event_entity_id_uses_event_id_unchanged(self): |
| 1129 | + with mock.patch('time.time', return_value=42.123), mock.patch( |
| 1130 | + 'uuid.uuid4', return_value='a68cf1ad-0393-4e18-af87-efe8f01a7c9c' |
| 1131 | + ): |
| 1132 | + event_obj = self.event_builder.create_conversion_event( |
| 1133 | + self.project_config, 'test_event', 'test_user', None, None, |
| 1134 | + ) |
| 1135 | + |
| 1136 | + snapshot = event_obj.params['visitors'][0]['snapshots'][0] |
| 1137 | + event = snapshot['events'][0] |
| 1138 | + self.assertEqual(event['entity_id'], self.project_config.get_event('test_event').id) |
0 commit comments