
    Yi{U                        d Z ddlZddlZddlmZ ddlmZ ddlmZ ddlm	Z	m
Z
 ddlmZ ddlmZ  G d	 d
      Z G d dej                         Z G d de      Z G d de      Z G d de      Z G d de      Z G d de      Z G d de      Z G d de      Z G d dej                         Zedk(  r ej6                          yy)z
Tests for FlagDefinitionCacheProvider functionality.

These tests follow the patterns from the TypeScript implementation in posthog-js/packages/node.
    N)Optional)mock)Client)FlagDefinitionCacheDataFlagDefinitionCacheProvider)GetResponse)FAKE_TEST_API_KEYc                   L    e Zd ZdZd Zdee   fdZdefdZ	deddfdZ
d
d	Zy)MockCacheProviderzAA mock implementation of FlagDefinitionCacheProvider for testing.c                     d | _         d| _        d| _        d| _        d| _        d| _        d | _        d | _        d | _        d | _	        y )NTr   )
stored_datashould_fetch_return_valueget_call_countshould_fetch_call_counton_received_call_countshutdown_call_countshould_fetch_error	get_erroron_received_errorshutdown_errorselfs    q/mnt/e/genesis-system/.venvs/browser-army/lib/python3.12/site-packages/posthog/test/test_flag_definition_cache.py__init__zMockCacheProvider.__init__   sQ    >B)-&'($&'##$ 7;.26:37    returnc                 t    | xj                   dz  c_         | j                  r| j                  | j                  S N   )r   r   r   r   s    r   get_flag_definitionsz&MockCacheProvider.get_flag_definitions$   s0    q >>.. r   c                 t    | xj                   dz  c_         | j                  r| j                  | j                  S r   )r   r   r   r   s    r   should_fetch_flag_definitionsz/MockCacheProvider.should_fetch_flag_definitions*   s4    $$)$"")))---r   dataNc                 l    | xj                   dz  c_         | j                  r| j                  || _        y r   )r   r   r   )r   r#   s     r   on_flag_definitions_receivedz.MockCacheProvider.on_flag_definitions_received0   s0    ##q(#!!(((r   c                 ^    | xj                   dz  c_         | j                  r| j                  y r   )r   r   r   s    r   shutdownzMockCacheProvider.shutdown6   s-      A% %%% r   )r   N)__name__
__module____qualname____doc__r   r   r   r    boolr"   r%   r'    r   r   r   r      sA    K
8 h/F&G  .t . 1H  T  &r   r   c                   H    e Zd ZdZed        Zed        Zd Zd Zde	fdZ
y)	TestFlagDefinitionCacheProviderz3Tests for the FlagDefinitionCacheProvider protocol.c                     t        j                  d      | _        t        j                  d      | _        | j                  j	                          | j                  j	                          y )Nzposthog.client.batch_postzposthog.consumer.batch_post)r   patchclient_post_patcherconsumer_post_patcherstartclss    r   
setUpClassz*TestFlagDefinitionCacheProvider.setUpClass?   sL     #'**-H"I$(JJ/L$M!%%'!!'')r   c                 l    | j                   j                          | j                  j                          y N)r2   stopr3   r5   s    r   tearDownClassz-TestFlagDefinitionCacheProvider.tearDownClassG   s&    $$&!!&&(r   c                 Z    t               | _        ddi dddi dgdddd	d
g iid| _        y )N	test-flagTkeyactivefilterszanother-flagFcompanyproject)01rE   
propertiesflagsgroup_type_mappingcohorts)r   cache_providersample_flags_datar   s    r   setUpz%TestFlagDefinitionCacheProvider.setUpL   sI    /1 $tC&%BG )2	"BlB/0;
r   c                      y r9   r-   r   s    r   tearDownz(TestFlagDefinitionCacheProvider.tearDownW   s    r   r   c                 >    t        t        d| j                  dd      S )z-Create a client with the mock cache provider.test-personal-keyTF)personal_api_keyflag_definition_cache_provider	sync_modeenable_local_evaluation)r   r	   rK   r   s    r   _create_client_with_cachez9TestFlagDefinitionCacheProvider._create_client_with_cache[   s$    0+/+>+>$)
 	
r   N)r(   r)   r*   r+   classmethodr7   r;   rM   rO   r   rV   r-   r   r   r/   r/   <   sA    =* * ) )	

6 
r   r/   c                       e Zd ZdZ ej
                  d      d        Z ej
                  d      d        Z ej
                  d      d        Z ej
                  d      d        Z	y)TestCacheInitializationz(Tests for cache initialization behavior.posthog.client.getc                    d| j                   _        | j                  | j                   _        | j	                         }|j                          |j                          | j                  | j                   j                  d       | j                  | j                   j                  d       | j                  t        |j                        d       | j                  |j                  d   d   d       |j                          y)zDWhen should_fetch returns False and cache has data, use cached data.Fr      r   r?   r=   N)rK   r   rL   r   rV   _load_feature_flagsassert_not_calledassertEqualr   r   lenfeature_flagsjoinr   mock_getclients      r   5test_uses_cached_data_when_should_fetch_returns_falsezMTestCacheInitialization.test_uses_cached_data_when_should_fetch_returns_falsei   s     9>5*.*@*@'//1""$ 	""$ 	,,DDaH,,;;Q? 	V112A6--a07Er   c                    d| j                   _        t        | j                  dd      |_        | j                         }|j                          |j                          | j                  | j                   j                  d       | j                  | j                   j                  d       | j                  | j                   j                  d       |j                          y)z/When should_fetch returns True, fetch from API.T	test-etagFr#   etagnot_modifiedr   r   N)rK   r   r   rL   return_valuerV   r]   assert_called_oncer_   r   r   r   rb   rc   s      r   4test_fetches_from_api_when_should_fetch_returns_truezLTestCacheInitialization.test_fetches_from_api_when_should_fetch_returns_true   s     9=5 +''k!
 //1""$ 	##% 	,,DDaH,,;;Q? 	,,CCQGr   c                 N   d| j                   _        d| j                   _        t        | j                  dd      |_        | j                         }|j                          |j                          | j                  | j                   j                  d       |j                          y)zMWhen should_fetch=False but cache is empty and no flags loaded, fetch anyway.FNrh   ri   r   )rK   r   r   r   rL   rl   rV   r]   rm   r_   r   rb   rc   s      r   5test_emergency_fallback_when_cache_empty_and_no_flagszMTestCacheInitialization.test_emergency_fallback_when_cache_empty_and_no_flags   s     9>5*.' +''k!
 //1""$ 	##% 	,,CCQGr   c                    d| j                   _        d| j                   _        | j                         }| j                  d   |_        | j                  d   |_        | j                  d   |_        |j                          |j                          | j                  t        |j
                        d       | j                  |j
                  d   d   d	       |j                          y)
zFWhen cache returns None but client has flags, preserve existing flags.FNrH   rI   rJ   r\   r   r?   r=   )rK   r   r   rV   rL   ra   rI   rJ   r]   r^   r_   r`   rb   rc   s      r   5test_preserves_existing_flags_when_cache_returns_nonezMTestCacheInitialization.test_preserves_existing_flags_when_cache_returns_none   s     9>5*.'//1  $55g>$($:$:;O$P!//	:""$ 	""$ 	V112A6--a07Er   N)
r(   r)   r*   r+   r   r1   rf   rn   rp   rr   r-   r   r   rY   rY   f   s    2TZZ$% &* TZZ$% &. TZZ$% &( TZZ$% &r   rY   c                       e Zd ZdZ ej
                  d      d        Z ej
                  d      d        Z ej
                  d      d        Z ej
                  d      d        Z	y)TestFetchCoordinationz-Tests for fetch coordination between workers.rZ   c                 x   d| j                   _        t        | j                  dd      |_        | j                         }|j                          | j                  | j                   j                  d       |j                          | j                  | j                   j                  d       |j                          y)z?should_fetch_flag_definitions is called before each poll cycle.Trh   Fri   r   r\   N)
rK   r   r   rL   rl   rV   r]   r_   r   rb   rc   s      r   (test_calls_should_fetch_before_each_pollz>TestFetchCoordination.test_calls_should_fetch_before_each_poll   s     9=5 +''k!
 //1 	""$,,DDaH 	""$,,DDaHr   c                    d| j                   _        | j                  | j                   _        | j	                         }|j                          | j                  | j                   j                  d       |j                          y)zAon_flag_definitions_received is NOT called when fetch is skipped.Fr   N)	rK   r   rL   r   rV   r]   r_   r   rb   rc   s      r   1test_does_not_call_on_received_when_fetch_skippedzGTestFetchCoordination.test_does_not_call_on_received_when_fetch_skipped   sg     9>5*.*@*@'//1""$ 	,,CCQGr   c                    d| j                   _        t        | j                  dd      |_        | j                         }|j                          | j                  | j                   j                  d       | j                  | j                   j                         | j                  t        | j                   j                  d         d       |j                          y)	z7on_flag_definitions_received receives the fetched data.Trh   Fri   r   rH   r\   N)rK   r   r   rL   rl   rV   r]   r_   r   assertIsNotNoner   r`   rb   rc   s      r   )test_stores_data_in_cache_after_api_fetchz?TestFetchCoordination.test_stores_data_in_cache_after_api_fetch   s     9=5 +''k!
 //1""$ 	,,CCQGT00<<=T00<<WEFJr   c                    d| j                   _        t        | j                  dd      |_        | j                         }|j                          | j                  | j                   j                  d       | j                  t        |j                        d       t        ddd      |_        |j                          | j                  |j                  d       | j                  | j                   j                  d       | j                  | j                   j                  d       | j                  t        |j                        d       |j                          y)z?When API returns 304 Not Modified, cache should not be updated.Trh   Fri   r   r\   N)rK   r   r   rL   rl   rV   r]   r_   r   r`   ra   
call_countr   rb   rc   s      r   +test_304_not_modified_does_not_update_cachezATestFetchCoordination.test_304_not_modified_does_not_update_cache   s!    9=5 !,''k!
 //1""$ 	,,CCQGV112A6 !,Kd!
 	""$ 	,,a0 	,,DDaH 	,,CCQG 	V112A6r   N)
r(   r)   r*   r+   r   r1   rv   rx   r{   r~   r-   r   r   rt   rt      s    7TZZ$% &( TZZ$% & TZZ$% &$ TZZ$%# &#r   rt   c                       e Zd ZdZ ej
                  d      d        Z ej
                  d      d        Z ej
                  d      d        Z ej
                  d      d        Z	y)TestErrorHandlingz6Tests for error handling in cache provider operations.rZ   c                 <   t        d      | j                  _        t        | j                  dd      |_        | j                         }|j                          |j                          | j                  t        |j                        d       |j                          y)z@When should_fetch throws an error, default to fetching from API.zLock acquisition failedrh   Fri   r\   N)	ExceptionrK   r   r   rL   rl   rV   r]   rm   r_   r`   ra   rb   rc   s      r   ,test_should_fetch_error_defaults_to_fetchingz>TestErrorHandling.test_should_fetch_error_defaults_to_fetching(  s     2;;T1U. +''k!
 //1""$ 	##% 	V112A6r   c                    d| j                   _        t        d      | j                   _        t	        | j
                  dd      |_        | j                         }|j                          |j                          |j                          y)z:When get_flag_definitions throws an error, fetch from API.FzCache read failedrh   ri   N)rK   r   r   r   r   rL   rl   rV   r]   rm   rb   rc   s      r   &test_get_error_falls_back_to_api_fetchz8TestErrorHandling.test_get_error_falls_back_to_api_fetch<  ss     9>5(12E(F% +''k!
 //1""$ 	##%r   c                    d| j                   _        t        d      | j                   _        t	        | j
                  dd      |_        | j                         }|j                          | j                  t        |j                        d       | j                  |j                  d   d   d	       |j                          y
)zDWhen on_flag_definitions_received throws, flags are still in memory.TzCache write failedrh   Fri   r\   r   r?   r=   N)rK   r   r   r   r   rL   rl   rV   r]   r_   r`   ra   rb   rc   s      r   ,test_on_received_error_keeps_flags_in_memoryz>TestErrorHandling.test_on_received_error_keeps_flags_in_memoryN  s     9=509:N0O- +''k!
 //1""$ 	V112A6--a07Er   c                    t        d      | j                  _        t        | j                  dd      |_        | j                         }|j                          |j                          | j                  | j                  j                  d       y)zBWhen shutdown throws an error, it's logged but shutdown continues.zLock release failedrh   Fri   r   N)r   rK   r   r   rL   rl   rV   r]   rb   r_   r   rc   s      r   +test_shutdown_error_is_logged_but_continuesz=TestErrorHandling.test_shutdown_error_is_logged_but_continuesa  sw     .77L-M* +''k!
 //1""$ 	 	,,@@!Dr   N)
r(   r)   r*   r+   r   r1   r   r   r   r   r-   r   r   r   r   %  s    @TZZ$% && TZZ$% &" TZZ$% &$ TZZ$%E &Er   r   c                       e Zd ZdZ ej
                  d      d        Z ej
                  d      d        Z ej
                  d      d        Zy)TestShutdownLifecyclezTests for shutdown lifecycle.rZ   c                     t        | j                  dd      |_        | j                         }|j	                          |j                          | j                  | j                  j                  d       y)z.Client shutdown calls cache provider shutdown.rh   Fri   r   N)	r   rL   rl   rV   r]   rb   r_   rK   r   rc   s      r   +test_shutdown_calls_cache_provider_shutdownzATestShutdownLifecycle.test_shutdown_calls_cache_provider_shutdownw  sa     !,''k!
 //1""$ 	,,@@!Dr   c                    d| j                   _        | j                  | j                   _        | j	                         }|j                          |j                          | j                  | j                   j                  d       y)z@Shutdown is called even when cache was used instead of fetching.Fr   N)	rK   r   rL   r   rV   r]   rb   r_   r   rc   s      r   *test_shutdown_called_even_without_fetchingz@TestShutdownLifecycle.test_shutdown_called_even_without_fetching  sg     9>5*.*@*@'//1""$ 	,,@@!Dr   c                 *   t        | j                  dd      |_        | j                         }|j	                          |j                          |j                          |j                          | j                  | j                  j                  d       y)zLCalling join() multiple times should only call cache provider shutdown once.rh   Fri   r   N)	r   rL   rl   rV   r]   rb   assertGreaterEqualrK   r   rc   s      r   +test_multiple_join_calls_only_shutdown_oncezATestShutdownLifecycle.test_multiple_join_calls_only_shutdown_once  su     !,''k!
 //1""$ 	 	 3 3 G GKr   N)	r(   r)   r*   r+   r   r1   r   r   r   r-   r   r   r   r   t  si    'TZZ$%E &E TZZ$%
E &
E TZZ$%L &Lr   r   c                   @    e Zd ZdZ ej
                  d      d        Zy)TestBackwardCompatibilityz8Tests for backward compatibility without cache provider.rZ   c                    t        | j                  dd      |_        t        t        ddd      }|j                          |j                          | j                  t        |j                        d       |j                          y)	z:Client works normally without a cache provider configured.rh   Fri   rQ   T)rR   rT   rU   r\   N)r   rL   rl   r   r	   r]   rm   r_   r`   ra   rb   rc   s      r   !test_works_without_cache_providerz;TestBackwardCompatibility.test_works_without_cache_provider  sx     !,''k!

 0$)	
 	""$ 	##% 	V112A6r   N)r(   r)   r*   r+   r   r1   r   r-   r   r   r   r     s#    BTZZ$% &r   r   c                       e Zd ZdZ ej
                  d      d        Z ej
                  d      d        Z ej
                  d      d        Z ej
                  d      d        Z	y)TestDataIntegrityz8Tests for data integrity between cache and client state.rZ   c                 T   d| j                   _        dddg ddgidgi i d| j                   _        | j                         }|j	                          | j                  t        |j                        d	       | j                  |j                  d   d
   d       |j                          y)z;Flags loaded from cache are available for local evaluation.Fr=   Tgroupsd   )rF   rollout_percentager>   rG   r   r?   N)
rK   r   r   rV   r]   r_   r`   ra   feature_flags_by_keyrb   rc   s      r   *test_cached_flags_available_for_evaluationz<TestDataIntegrity.test_cached_flags_available_for_evaluation  s     9>5 '" .069#  #%!+
'& //1""$ 	V112A644[A%H+Vr   c                 8   d| j                   _        | j                  | j                   _        | j	                         }|j                          | j                  |j                  d   d       | j                  |j                  d   d       |j                          y)z2Group type mapping is correctly loaded from cache.FrD   rB   rE   rC   N)	rK   r   rL   r   rV   r]   r_   rI   rb   rc   s      r   )test_group_type_mapping_loaded_from_cachez;TestDataIntegrity.test_group_type_mapping_loaded_from_cache  s     9>5*.*@*@'//1""$2237C2237Cr   c                     d| j                   _        | j                  | j                   _        | j	                         }|j                          | j                  d|j                         |j                          y)z(Cohorts are correctly loaded from cache.FrE   N)	rK   r   rL   r   rV   r]   assertInrJ   rb   rc   s      r   test_cohorts_loaded_from_cachez0TestDataIntegrity.test_cohorts_loaded_from_cache  s[     9>5*.*@*@'//1""$c6>>*r   c                    ddi dgi i d}|| j                   _        d| j                   _        | j                         }|j	                          | j                  |j                  d   d   d       | j                  | j                   j                  d       d| j                   _        ddi dgd	d
iddg iid}t        |dd      |_	        |j	                          | j                  |j                  d   d   d       | j                  |j                  d	   d
       | j                  | j                   j                  d       | j                  | j                   j                  d   d   d   d       |j                          y)zIState transition: cache has old data -> API returns new -> cache updated.zold-flagTr>   rG   Fr   r?   znew-flagrD   rB   rE   rF   znew-etagri   r   rH   N)rK   r   r   rV   r]   r_   ra   r   r   rl   rI   rb   )r   rd   old_flags_datare   new_flags_datas        r   ,test_cache_updated_when_api_returns_new_dataz>TestDataIntegrity.test_cache_updated_when_api_returns_new_data  s   
 )DRHI"$3

 +9'8=5//1 	""$--a07D,,CCQG 9=5(DRHI#&	"2lB/03

 !,ju!
 	""$ 	--a07D2237C 	,,CCQG,,88A!DUKZXr   N)
r(   r)   r*   r+   r   r1   r   r   r   r   r-   r   r   r   r     s    BTZZ$% &> TZZ$% & TZZ$%
 &
 TZZ$%' &'r   r   c                   @    e Zd ZdZ ej
                  d      d        Zy)TestConcurrencyz.Tests for thread safety and concurrent access.rZ   c                    t        | j                  dd      |_        | j                         g fd}t	        d      D cg c]  }t        j                  |       }}|D ]  }|j                           |D ]  }|j                           | j                  t              dd        | j                  j                         | j                  t        j                        d	       j                          y
c c}w )zEMultiple threads calling _load_feature_flags should not cause errors.rh   Fri   c                  v    	 j                          y # t        $ r} j                  |        Y d } ~ y d } ~ ww xY wr9   )r]   r   append)ere   errorss    r   
load_flagszUTestConcurrency.test_concurrent_load_feature_flags_is_thread_safe.<locals>.load_flags9  s3    !**, !a  !s    	838   )targetr   zUnexpected errors: r\   N)r   rL   rl   rV   range	threadingThreadr4   rb   r_   r`   rz   ra   )r   rd   r   _threadstre   r   s         @@r   1test_concurrent_load_feature_flags_is_thread_safezATestConcurrency.test_concurrent_load_feature_flags_is_thread_safe/  s     !,''k!
 //1	! AFaI19##:6II 	AGGI	 	AFFH	 	Va+>vh)GH 	V112V112A6 Js   DN)r(   r)   r*   r+   r   r1   r   r-   r   r   r   r   ,  s#    8TZZ$% &r   r   c                       e Zd ZdZd Zd Zy)TestProtocolCompliancezTests for Protocol compliance.c                 D    t               }| j                  |t               y)zAMockCacheProvider satisfies FlagDefinitionCacheProvider protocol.N)r   assertIsInstancer   )r   providers     r   'test_mock_provider_is_protocol_instancez>TestProtocolCompliance.test_mock_provider_is_protocol_instanceS  s    $&h(CDr   c                 R     G d d      } |       }| j                  |t               y)z;Class missing methods is not a FlagDefinitionCacheProvider.c                       e Zd Zd Zy)dTestProtocolCompliance.test_incomplete_provider_is_not_protocol_instance.<locals>.IncompleteProviderc                      y r9   r-   r   s    r   r    zyTestProtocolCompliance.test_incomplete_provider_is_not_protocol_instance.<locals>.IncompleteProvider.get_flag_definitions\  s    r   N)r(   r)   r*   r    r-   r   r   IncompleteProviderr   [  s    r   r   N)assertNotIsInstancer   )r   r   r   s      r   1test_incomplete_provider_is_not_protocol_instancezHTestProtocolCompliance.test_incomplete_provider_is_not_protocol_instanceX  s'    	 	 &'  +FGr   N)r(   r)   r*   r+   r   r   r-   r   r   r   r   P  s    (E
Hr   r   __main__)r+   r   unittesttypingr   r   posthog.clientr   posthog.flag_definition_cacher   r   posthog.requestr   posthog.test.test_utilsr	   r   TestCaser/   rY   rt   r   r   r   r   r   r   r(   mainr-   r   r   <module>r      s        ! ( 5$& $&N'
h&7&7 '
T\= \~]; ]@LE7 LE^0L; 0Lf ? 8f7 fR!5 !HHX.. H& zHMMO r   