@@ -86,7 +86,6 @@ def setUp(self):
8686 super ().setUp ()
8787 self .event_logger = mock .Mock (spec = DebugLogger )
8888 self .mock_span = mock .MagicMock (spec = ServerSpan )
89- self .mock_span .context = None
9089
9190 def test_make_client_without_timeout_set (self , file_watcher_mock ):
9291 with create_temp_config_file ({}) as f :
@@ -219,7 +218,8 @@ def test_make_object_for_context_and_decider_context(self):
219218 self .assertEqual (decider_event_dict ["canonical_url" ], CANONICAL_URL )
220219 self .assertEqual (decider_event_dict ["request" ]["canonical_url" ], CANONICAL_URL )
221220
222- def test_make_object_for_context_and_decider_context_without_span (self ):
221+ @mock .patch ("reddit_decider.experiments_client_counter.labels" )
222+ def test_make_object_for_context_without_span (self , metric_counter_labels ):
223223 with create_temp_config_file ({}) as f :
224224 decider_ctx_factory = decider_client_from_config (
225225 {"experiments.path" : f .name , "experiments.timeout" : "2 seconds" },
@@ -236,13 +236,56 @@ def test_make_object_for_context_and_decider_context_without_span(self):
236236 assert len (captured .records ) == 1
237237 self .assertEqual (["WARNING:root:Dummy warning" ], captured .output )
238238
239+ metric_counter_labels .assert_called_once_with (
240+ operation = "make_object_for_context" ,
241+ success = "false" ,
242+ error_type = "missing:'span'" ,
243+ pkg_version = mock .ANY ,
244+ )
245+
239246 self .assertIsInstance (decider , Decider )
240247
241248 decider_ctx_dict = decider ._decider_context .to_dict ()
242249 self .assertEqual (decider_ctx_dict ["user_id" ], None )
243250
244- def test_make_object_for_context_and_decider_context_with_broken_decider_field_extractor (self ):
245- def broken_decider_field_extractor (_request : RequestContext ):
251+ @mock .patch ("reddit_decider.experiments_client_counter.labels" )
252+ def test_make_object_for_context_with_span_context_as_None (self , metric_counter_labels ):
253+ with create_temp_config_file ({}) as f :
254+ decider_ctx_factory = decider_client_from_config (
255+ {"experiments.path" : f .name , "experiments.timeout" : "2 seconds" },
256+ self .event_logger ,
257+ prefix = "experiments." ,
258+ request_field_extractor = decider_field_extractor ,
259+ )
260+
261+ mock_span = mock .MagicMock (spec = ServerSpan )
262+ # span is missing context
263+
264+ with self .assertLogs (logger , logging .WARN ) as captured :
265+ # ensure no warnings are printed except for the dummy one
266+ # https://stackoverflow.com/a/61381576/4260179
267+ logger .warning ("Dummy warning" )
268+
269+ decider = decider_ctx_factory .make_object_for_context (name = "test" , span = mock_span )
270+ assert len (captured .records ) == 1
271+ self .assertEqual (["WARNING:root:Dummy warning" ], captured .output )
272+
273+ metric_counter_labels .assert_called_once_with (
274+ operation = "make_object_for_context" ,
275+ success = "false" ,
276+ error_type = "missing:'span.context'" ,
277+ pkg_version = mock .ANY ,
278+ )
279+
280+ self .assertIsInstance (decider , Decider )
281+
282+ decider_ctx_dict = decider ._decider_context .to_dict ()
283+ self .assertEqual (decider_ctx_dict ["user_id" ], None )
284+
285+ def test_make_object_for_context_and_decider_context_with_malformed_decider_field_extractor (
286+ self ,
287+ ):
288+ def decider_field_extractor_with_malformed_fields (_request : RequestContext ):
246289 return {
247290 "app_name" : {},
248291 "build_number" : BUILD_NUMBER ,
@@ -256,7 +299,7 @@ def broken_decider_field_extractor(_request: RequestContext):
256299 {"experiments.path" : f .name , "experiments.timeout" : "2 seconds" },
257300 self .event_logger ,
258301 prefix = "experiments." ,
259- request_field_extractor = broken_decider_field_extractor ,
302+ request_field_extractor = decider_field_extractor_with_malformed_fields ,
260303 )
261304
262305 with self .assertLogs () as captured :
@@ -280,6 +323,39 @@ def broken_decider_field_extractor(_request: RequestContext):
280323 for x in captured .records
281324 )
282325
326+ @mock .patch ("reddit_decider.experiments_client_counter.labels" )
327+ def test_make_object_for_context_with_broken_decider_field_extractor_raises_exception (
328+ self , metric_counter_labels
329+ ):
330+ class SomeException (Exception ):
331+ pass
332+
333+ def broken_decider_field_extractor (_request : RequestContext ):
334+ raise SomeException ("bad extractor" )
335+
336+ with create_temp_config_file ({}) as f :
337+ decider_ctx_factory = decider_client_from_config (
338+ {"experiments.path" : f .name , "experiments.timeout" : "2 seconds" },
339+ self .event_logger ,
340+ prefix = "experiments." ,
341+ request_field_extractor = broken_decider_field_extractor ,
342+ )
343+
344+ with self .assertRaises (SomeException ) as e :
345+ decider_ctx_factory .make_object_for_context (name = "test" , span = self .mock_span )
346+
347+ self .assertEqual (
348+ str (e .exception ),
349+ "bad extractor" ,
350+ )
351+
352+ metric_counter_labels .assert_called_once_with (
353+ operation = "make_object_for_context" ,
354+ success = "false" ,
355+ error_type = "request_field_extractor" ,
356+ pkg_version = mock .ANY ,
357+ )
358+
283359
284360# Todo: test DeciderClient()
285361# @mock.patch("reddit_decider.FileWatcher")
0 commit comments