@@ -395,6 +395,81 @@ def test_flag_called_event_carries_parsed_local_payload(
395395 self .assertEqual (properties ["$feature_flag_payload" ], {"copy" : "new" })
396396
397397
398+ class TestEvaluateFlagsMissingLocalDefinition (unittest .TestCase ):
399+ def setUp (self ):
400+ self .client = Client (FAKE_TEST_API_KEY , secret_key = "test" )
401+ self .client .feature_flags = [
402+ {
403+ "id" : 1 ,
404+ "name" : "Local flag" ,
405+ "key" : "local-flag" ,
406+ "active" : True ,
407+ "filters" : {"groups" : [{"properties" : [], "rollout_percentage" : 100 }]},
408+ }
409+ ]
410+
411+ def tearDown (self ):
412+ self .client .shutdown ()
413+
414+ @staticmethod
415+ def _remote_response ():
416+ return {
417+ "flags" : {
418+ "local-flag" : {"enabled" : False , "variant" : None },
419+ "remote-only" : {"enabled" : True , "variant" : None },
420+ "unrequested" : {"enabled" : True , "variant" : None },
421+ }
422+ }
423+
424+ @mock .patch ("posthog.client.flags" )
425+ def test_missing_requested_key_triggers_one_scoped_fallback (self , patch_flags ):
426+ patch_flags .return_value = self ._remote_response ()
427+ requested_keys = ["local-flag" , "remote-only" ]
428+
429+ flags = self .client .evaluate_flags ("user-1" , flag_keys = requested_keys )
430+
431+ self .assertEqual (set (flags .keys ), set (requested_keys ))
432+ self .assertTrue (flags .get_flag ("local-flag" ))
433+ self .assertTrue (flags .get_flag ("remote-only" ))
434+ patch_flags .assert_called_once ()
435+ self .assertEqual (
436+ patch_flags .call_args .kwargs ["flag_keys_to_evaluate" ], requested_keys
437+ )
438+
439+ @mock .patch ("posthog.client.flags" )
440+ def test_missing_requested_key_is_omitted_for_local_only_evaluation (
441+ self , patch_flags
442+ ):
443+ patch_flags .return_value = self ._remote_response ()
444+
445+ flags = self .client .evaluate_flags (
446+ "user-1" ,
447+ flag_keys = ["local-flag" , "remote-only" ],
448+ only_evaluate_locally = True ,
449+ )
450+
451+ self .assertEqual (flags .keys , ["local-flag" ])
452+ self .assertTrue (flags .get_flag ("local-flag" ))
453+ self .assertIsNone (flags .get_flag ("remote-only" ))
454+ patch_flags .assert_not_called ()
455+
456+ @mock .patch ("posthog.client.flags" )
457+ def test_server_missing_key_falls_back_once_per_evaluation_call (self , patch_flags ):
458+ patch_flags .return_value = {"flags" : {}}
459+ requested_keys = ["local-flag" , "typo-flag" ]
460+
461+ first = self .client .evaluate_flags ("user-1" , flag_keys = requested_keys )
462+ second = self .client .evaluate_flags ("user-1" , flag_keys = requested_keys )
463+
464+ for flags in (first , second ):
465+ self .assertEqual (flags .keys , ["local-flag" ])
466+ self .assertTrue (flags .get_flag ("local-flag" ))
467+ self .assertIsNone (flags .get_flag ("typo-flag" ))
468+ self .assertEqual (patch_flags .call_count , 2 )
469+ for call in patch_flags .call_args_list :
470+ self .assertEqual (call .kwargs ["flag_keys_to_evaluate" ], requested_keys )
471+
472+
398473class TestEvaluateFlagsLocalDeviceBucketing (unittest .TestCase ):
399474 def setUp (self ):
400475 self .client = Client (FAKE_TEST_API_KEY )
0 commit comments