
    P1i`                         d dl mZm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mZmZ d dlZd dlmZ d d	lmZmZmZ d d
lmZ  e       Z G d de      Z G d de      Zy)    )TestCaseClientget_user_model)cache)reverse)patch	MagicMock)datedatetime	timedeltaN)Person)evaluate_good_day_get_cache_version_get_monthly_cache_keys)bzc                       e Zd ZdZd Zd Zd Zd Zd Zd Z	 e
d      d	        Zd
 Z e
d      d        Zd Zd Zd Zd Zd Zy)BaziGoodDaysCachingTestsz
    Test caching behavior for BaZi good days feature.
    Verifies cache hits for identical stem/branch across different users,
    and correct union of branch/stem cache results.
    c           	      4   t         j                  j                  ddd      | _        t         j                  j                  ddd      | _        t
        j                  j                  ddt        d	d
d
      d| j                        | _        dddddddddd| j                  _	        | j                  j                          t
        j                  j                  ddt        ddd      d| j                        | _        dddddddddd| j                  _	        | j                  j                          t         j                  j                  ddd      | _        t
        j                  j                  ddt        ddd      d| j                        | _        d
d
dd
d
dd
d
dd| j                  _	        | j                  j                          t               | _        d| _        t#        j$                          t#        j&                  dd
d        y )N
1111111111user1@example.compw123456phoneemailpassword
2222222222user2@example.com
User1OwnerNi     T)namegender
birth_dateowner
created_byr   )godearth)yearmonthday
User2Owneri           
3333333333zuser3@example.com
User3Owneri     
   z/api/bazi/good-days/bz_good_days_version)Userobjectscreate_useruser1user2r   creater   person1bazi_resultsaveperson2person3person3_bazir   clienturlr   clearsetselfs    G/home/cursorai/projects/iching/api/tests/test_bazi_good_days_caching.pysetUpzBaziGoodDaysCachingTests.setUp   s   \\--&9J . 

 \\--&9J . 

 ~~,,D!Q'zz - 
 *+q)$
 
 	~~,,D!R(zz - 
 *+q)$
 
 	 ||//&9J 0 
 #NN11D!R(|| 2 
 *+q))
%
 	 h) 			(!T2    c                 ,    t        j                          y )N)r   rC   rE   s    rG   tearDownz!BaziGoodDaysCachingTests.tearDownW   s    rI   c                     t               }| j                  |d       t        j                  ddd       t               }| j                  |d       y)z,Test cache version retrieval and management.r!   r4   r-   N)r   assertEqualr   rD   )rF   versions     rG   test_cache_version_managementz6BaziGoodDaysCachingTests.test_cache_version_managementZ   sG     %&!$ 			(!T2$&!$rI   c                     d\  }}d}t        |||      \  }}d}d}| j                  ||       | j                  ||       y)zATest that cache keys are correctly generated for branch and stem.r   r   202501bz:v1:good:branch:0:202501zbz:v1:good:stem:0:202501N)r   rM   )rF   day_gday_eyyyymm
branch_keystem_keyexpected_branchexpected_stems           rG   "test_monthly_cache_keys_generationz;BaziGoodDaysCachingTests.test_monthly_cache_keys_generatione   sM    u6ueVL
H62_5=1rI   c                    d\  }}d}t        |||      \  }}t        j                  ddd       t        |||      \  }}| j                  ||       | j                  ||       | j	                  d|       | j	                  d|       y)z9Test that different cache versions create different keys.rQ   rR   r4   r/   Nzv1:v2:)r   r   rD   assertNotEqualassertIn)rF   rT   rU   rV   branch_key_v1stem_key_v1branch_key_v2stem_key_v2s           rG   $test_cache_key_separation_by_versionz=BaziGoodDaysCachingTests.test_cache_key_separation_by_versionr   s    u &=UE6%R"{ 			(!T2 &=UE6%R"{M=9K5e]+e]+rI   c                     d}t        dd|      \  }}t        dd|      \  }}| j                  ||       | j                  d|       | j                  d|       | j                  ||       | j                  d|       | j                  d|       y)	z>Test that different day pillars generate different cache keys.rR   r   r!   z
:branch:0:z
:branch:1:z:stem:0:z:stem:1:N)r   r^   r_   )rF   rV   branch_key_1
stem_key_1branch_key_2
stem_key_2s         rG   *test_cache_separation_by_pillar_componentszCBaziGoodDaysCachingTests.test_cache_separation_by_pillar_components   s     $;1a#H j $;1a#H j 	L,7lL1lL1 	J
3j*-j*-rI   Aiching.utils.person_good_days.bz_utils.getCalendar10kGodEarthStemc                 |   ddddddddddddd|_         | j                  j                  | j                         | j                  j	                  | j
                  ddd      }| j                  |j                  d	       |j                         }|j                          | j                  j                  | j                         | j                  j	                  | j
                  ddd      }| j                  |j                  d	       |j                         }| j                  t        |d
         t        |d
                |d
   rq|d
   rk|d
   d   }|d
   d   }| j                  |d   |d          | j                  |d   |d          | j                  t        |d         t        |d                yyy)z@Test that users with identical day pillars reuse cached results.r   ger!      r)   r*   r+   hour
2025-01-01
2025-01-02startend   daysr   is_goodreasonsN)return_valuerA   force_loginr8   getrB   rM   status_codejson
reset_mockr9   len)rF   mock_calendar	response1data1	response2data2day1day2s           rG   )test_cache_reuse_across_users_same_pillarzBBaziGoodDaysCachingTests.test_cache_reuse_across_users_same_pillar   s   
 !$1%#!$	&
" 	

+KKOODHH!/
 	 	..4  	  " 	

+KKOODHH!/
 	 	..4  	U6]+Sv-?@=U6]=#D=#DT&\4<8T)_d9o>Si13tI3GH +=rI   c                 d   | j                   j                  | j                         | j                   j                  | j                  ddd      }| j                  |j                  d       | j                   j                  | j                         | j                   j                  | j                  ddd      }| j                  |j                  d       |j                         }|j                         }| j                  |d   d   |d   d          | j                  |d   d   |d   d          y)	z=Test that users with different day pillars don't share cache.rs   rt   ru   rx   userday_stem
day_branchN)
rA   r}   r8   r~   rB   rM   r   r?   r   r^   )rF   r   	response3r   data3s        rG   !test_cache_miss_different_pillarsz:BaziGoodDaysCachingTests.test_cache_miss_different_pillars   s    	

+KKOODHH!/
 	 	..4 	-KKOODHH!/
 	 	..4    	E&M*5uV}Z7PQE&M,7v|9TUrI   #iching.utils.person_good_days.cachec           	         d|j                   _        d|j                  _        ddlm} ddlm}  |dd |ddd            \  }}|j                   j                  }|D cg c]  }|d   s	|d   d    }}t        d |D              }	t        d |D              }
| j                  |	d	|        | j                  |
d
|        yc c}w )zCTest that branch-only and stem-dependent rules use separate caches.Nr   get_month_reasons_cachedr     r!   c              3   $   K   | ]  }d |v  
 yw):branch:N .0keys     rG   	<genexpr>zKBaziGoodDaysCachingTests.test_branch_vs_stem_cache_usage.<locals>.<genexpr>   s     "N:#4   c              3   $   K   | ]  }d |v  
 yw):stem:Nr   r   s     rG   r   zKBaziGoodDaysCachingTests.test_branch_vs_stem_cache_usage.<locals>.<genexpr>   s      JMSSMr   zNo branch cache key found in: zNo stem cache key found in: )
r~   r|   rD   iching.utils.person_good_daysr   r   r   call_args_listany
assertTrue)rF   
mock_cacher   r   
branch_mapstem_map	get_callscallget_call_keysbranch_pattern_foundstem_pattern_founds              rG   test_branch_vs_stem_cache_usagez8BaziGoodDaysCachingTests.test_branch_vs_stem_cache_usage   s     '+
#&*
# 	K!  81d4A>NO
H NN11	09E	T!Wa	E  #"N"NN  JM JJ,0N}o.^_*.J=/,Z[ Fs   
C)
Cc                 .   | j                   j                  | j                         | j                   j                  | j                  ddd      }| j                  |j                  d       | j                   j                  | j                  ddd      }| j                  |j                         |j                                t        j                  dd      }t        j                  d|dz   d       | j                   j                  | j                  ddd      }| j                  |j                  d       | j                  |j                         d   |j                         d          t        j                  dd      }| j                  ||dz          y)	z<Test that bumping cache version makes old entries invisible.rs   rt   ru   rx   r4   r!   Nry   )
rA   r}   r8   r~   rB   rM   r   r   r   rD   )rF   r   response1_repeatcurrent_versionr   new_versions         rG   (test_cache_invalidation_via_version_bumpzABaziGoodDaysCachingTests.test_cache_invalidation_via_version_bump   sV    	

+KKOODHH!/
 	 	..4  ;;??488!6
  	)+;+@+@+BC  ))$:A>		(/A*=tD KKOODHH!/
 	 	..4 	)&19>>3CF3KL ii 6:o&9:rI   c                     ddl m} ddlm} t	        j
                  ddd        |       } |d|       t	        j                  dd	      }| j                  |d
       |j                         }| j                  d|       y)z/Test the management command for clearing cache.r   )call_command)StringIOr4   r2   Nclear_bazi_good_days_cache)stdoutr!   rp   z#bz_good_days_version bumped: 3 -> 4)
django.core.managementr   ior   r   rD   r~   rM   getvaluer_   )rF   r   r   outr   outputs         rG   #test_management_command_cache_clearz<BaziGoodDaysCachingTests.test_management_command_cache_clear  sn    7 			(!T2 j1#> ii 6:a( ;VDrI   c                    | j                   j                  | j                         | j                   j                  | j                  ddd      }| j                  |j                  d       | j                   j                  | j                  ddd      }| j                  |j                  d       | j                  t        |j                         d         dkD         | j                  t        |j                         d         dkD         y	)
zFTest that cache operates on monthly blobs rather than individual days.rs   
2025-01-05ru   rx   z
2025-01-10z
2025-01-15ry   r   N)
rA   r}   r8   r~   rB   rM   r   r   r   r   )rF   responser   s      rG    test_monthly_cache_blob_behaviorz9BaziGoodDaysCachingTests.test_monthly_cache_blob_behavior.  s     	

+;;??488!.
  	--s3 KKOODHH!/
 	 	..4 	HMMOF34q89INN,V459:rI   c           	          ddl m} ddlm}  |dd |ddd            \  }}| j	                  |t
               | j	                  |t
               | j                  d       y)zFTest that final results correctly union branch and stem cache sources.r   r   r   r   r!   TN)r   r   r   r   assertIsInstancedictr   )rF   r   r   r   r   s        rG   +test_union_of_branch_and_stem_cache_resultszDBaziGoodDaysCachingTests.test_union_of_branch_and_stem_cache_resultsC  sY     	K!  81d4A>NO
H 	j$/h- 	rI   c                 F   t        d      5 }ddddddddddddd|_        | j                  j                  | j                         | j                  j                  | j                  ddd      }| j                  |j                  d	       |j                  }|j                          | j                  j                  | j                  d
dd      }| j                  |j                  d	       |j                  }| j                  ||       ddd       y# 1 sw Y   yxY w)z9Test that monthly caching reduces redundant calculations.rk   r   rm   r!   rq   rs   r   ru   rx   
2025-01-03z
2025-01-07N)r	   r|   rA   r}   r8   r~   rB   rM   r   
call_countr   assertLessEqual)rF   r   r   first_call_countr   second_call_counts         rG   'test_cache_performance_monthly_behaviorz@BaziGoodDaysCachingTests.test_cache_performance_monthly_behaviorT  s   VW[h  a( q)Q'a(	*M& KK##DJJ/%#3 I Y22C8,77$$& %#3 I Y22C8 - 8 8   !24DE? XWWs   DDD N)__name__
__module____qualname____doc__rH   rK   rO   r[   rd   rj   r	   r   r   r   r   r   r   r   r   r   rI   rG   r   r      s    >3@	%2,&.( NO(I P(ITV4 01\ 2\0!;FE(;*"!FrI   r   c                   L    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zd Zy)CacheKeyFormatTestsz.Test cache key format and collision avoidance.c                    ddl m} ddlm}m}  |       }|j
                  j                  ddd      | _        |j
                  j                  dd	d      | _        t        j
                  j                  d
 |ddd       |dd      | j                  d      | _        | j                  j                          | j                  j                          t        j
                  j                  d |ddd       |dd      | j                  d      | _        | j                  j                          | j                  j                          t        d      | _        y)z6Set up test users and data for cache key format tests.r   r   )r   timer   r   r   r   r   r   zUser1 Personi  r/   r3   T)r"   r$   
birth_timer&   r%   zUser2 Person   zapi:bazi-good-daysN)django.contrib.authr   r   r   r   r6   r7   r8   r9   r   r:   r;   calculate_bazir=   r>   r   rB   )rF   r   r   r   r5   s        rG   rH   zCacheKeyFormatTests.setUp{  s)   6' \\--&9J . 

 \\--&9J . 

 ~~,,D!Q'B{zz - 
 	##%~~,,D!Q'B{zz - 
 	##% /0rI   c                 &   d\  }}d}t        |||      \  }}| j                  |d       | j                  d| d|       | j                  d| |       | j                  |d       | j                  d| d|       | j                  d| |       y)	z4Test that cache keys follow expected format pattern.)r-   	   202312z^bz:v\d+:good:branch:\d+:\d{6}$r   :z^bz:v\d+:good:stem:\d+:\d{6}$r   N)r   assertRegexr_   )rF   rT   rU   rV   rW   rX   s         rG   !test_cache_key_format_consistencyz5CacheKeyFormatTests.test_cache_key_format_consistency  s    u6ueVL
H 	%GHq):6&lJ/ 	#CDugQ'2&lH-rI   c                    g d}g }|D ])  \  }}}t        |||      \  }}|j                  ||g       + t        |      }| j                  t	        |      ddt	        |              | j                  t	        |      t	        |      d       y)zDTest that different meaningful inputs generate different cache keys.))r   r/   rR   )r!   r2   rR   )r/   rp   202502)r2   r-   202503   zExpected 8 unique keys, got zFound duplicate cache keysN)r   extendrD   rM   r   )	rF   
test_casesall_keysrT   rU   rV   rW   rX   unique_keyss	            rG   "test_cache_key_collision_avoidancez6CacheKeyFormatTests.test_cache_key_collision_avoidance  s    

 $. E5&#:5%#P JOOZ23 %/
 (m 	[)10LSQ\M]L^._` 	[)3x=:VWrI   c                 d   | j                   j                  | j                         | j                   j                  | j                  ddd      }| j                  |j                  d       | j                   j                  | j                         | j                   j                  | j                  ddd      }| j                  |j                  d       |j                         }|j                         }| j                  |d   d   |d   d          | j                  |d   d   |d   d          y)	zAVerify that users with identical day pillars share cache entries.rs   rt   ru   rx   r   r   r   N)	rA   r}   r8   r~   rB   rM   r   r9   r   )rF   r   r   r   r   s        rG   *test_cross_user_cache_sharing_verificationz>CacheKeyFormatTests.test_cross_user_cache_sharing_verification  s    	

+KKOODHH!/
 	 	..4 	

+KKOODHH!/
 	 	..4   vz2E&M*4MNv|4eFmL6QRrI   c                 <   | j                   j                  | j                         | j                   j                  | j                  ddd      }| j                  |j                  d       |j                         }|d   D cg c]  }|d   	 }}|D cg c]  }|j                  d      s| }}|D cg c]  }|j                  d      s| }}| j                  t        |      d	kD  d
       | j                  t        |      d	kD  d       yc c}w c c}w c c}w )z7Test cache behavior when requests span multiple months.z
2025-01-30z
2025-02-02ru   rx   ry   r   z2025-01z2025-02r   zShould have January dateszShould have February datesN)rA   r}   r8   r~   rB   rM   r   r   
startswithr   r   )rF   r   datar+   datesd	jan_dates	feb_datess           rG   +test_cache_behavior_across_month_boundariesz?CacheKeyFormatTests.test_cache_behavior_across_month_boundaries  s    

+ ;;??488!.
  	--s3}} )-V5V5 %A1i)@Q	A %A1i)@Q	AI*,GHI*,HI 6AAs   DD+D5DDc           	      B   | j                   j                  | j                         g d}|D ]q  }t        |dd       }t        |dd       }| j                   j	                  | j
                  | d|dd| d|ddd      }| j                  |j                  d	       s y)
zDTest cache behavior under memory pressure (many different requests).)rR   r   r   202504202505Nrp   -02dz-01z-02ru   rx   )rA   r}   r8   intr~   rB   rM   r   )rF   monthsr*   r)   	month_numr   s         rG   %test_cache_memory_pressure_simulationz9CacheKeyFormatTests.test_cache_memory_pressure_simulation  s    

+ DEuRay>DE!"II{{txx 69S/5q3s32 H X1137 rI   c                    | j                   j                  | j                         ddd}g }t        d      D ]c  }| j                   j	                  | j
                  |      }| j                  |j                  d       |j                  |j                                e t        dt        |            D ]>  }| j                  |d   d   ||   d          | j                  |d   d	   ||   d	          @ y
)zATest that cache results are consistent across multiple API calls.rs   r   ru   r2   rx   r!   r   ry   r   N)rA   r}   r8   ranger~   rB   rM   r   appendr   r   )rF   
url_params	responsesiresps        rG   'test_cache_consistency_across_api_callsz;CacheKeyFormatTests.test_cache_consistency_across_api_calls  s    

+  ,LA
	qA;;??488Z8DT--s3TYY[)  q#i.)AYq\&19Q<3GHYq\&19Q<3GH *rI   c                     | j                   j                  | j                         | j                   j                  | j                  ddd      }| j                  |j                  d       t        j                  dddid       t        j                  d	d
d       t        j                  d      }| j                  |ddi       ddl	m
}  |ddd      \  }}| j                  d|       t        j                  |      }| j                  |       y)z>Test that cache invalidation only affects the current version.rs   rt   ru   rx   rS   testdata_v1  r4   r/   Nr   )r   rR   r]   )rA   r}   r8   r~   rB   rM   r   r   rD   r   r   r_   assertIsNone)rF   r   old_datar   new_branch_key_new_datas          rG   0test_cache_invalidation_preserves_other_versionszDCacheKeyFormatTests.test_cache_invalidation_preserves_other_versions  s     	

+KKOODHH!/
 	 	..4 			.0CTJ 			(!T2 999:FI#67 	J3Aq(Ce^,99^,(#rI   c                    t        d      5 }d|j                  _        d|j                  _        | j                  j                  | j                         | j                  j                  | j                  ddd      }| j                  |j                  d       |j                         }| j                  d|       | j                  d|       ddd       y# 1 sw Y   yxY w)	z4Test behavior when cache returns empty/None results.r   NTrs   ru   rx   ry   r   )r	   r~   r|   rD   rA   r}   r8   rB   rM   r   r   r_   )rF   r   r   r   s       rG   "test_edge_case_empty_cache_resultsz6CacheKeyFormatTests.test_edge_case_empty_cache_results5  s    89Z*.JNN'*.JNN'KK##DJJ/{{txx%#2 H X1137==?DMM&$'MM&$' :99s   CCCc                    t        j                  ddd       t        d      5 }dgfd}||j                  _        d|j                  _        | j                  j                  | j                         | j                  j                  | j                  ddd	      }| j                  |j                  d
       ddd       y# 1 sw Y   yxY w)z(Test recovery from malformed cache data.rS   malformed_string_not_dictr  r   r   c                  2    dxx   dz  cc<   d   dk(  ryy )Nr   r!   r  r   )argskwargsr   s     rG   mock_getzHCacheKeyFormatTests.test_malformed_cache_data_recovery.<locals>.mock_getO  s#    1"a=A%6rI   Trs   ru   rx   N)r   rD   r	   r~   side_effectr|   rA   r}   r8   rB   rM   r   )rF   r   r  r   r   s       @rG   "test_malformed_cache_data_recoveryz6CacheKeyFormatTests.test_malformed_cache_data_recoveryG  s     			.0KTR89ZJ *2JNN&*.JNN'KK##DJJ/{{txx%#2 H X1137' :99s   BCCN)r   r   r   r   rH   r   r   r   r   r   r  r  r  r  r   rI   rG   r   r   x  s<    8&1P."X0S0J(8 I$$6($8rI   r   )django.testr   r   r   r   django.core.cacher   django.urlsr   unittest.mockr	   r
   r   r   r   r   bazi.modelsr   r   r   r   r   iching.utilsr   bzur5   r   r   r   rI   rG   <module>r     sP    ( . #  * . .   h h "eFx eFPg8( g8rI   