
    Li2                       d Z ddlmZ ddlZddlZddlZddlZej                  j                  dej                  j                  ej                  j                  e      d             ddlmZ ddlmZmZ ddlZddlmZ 	 	 	 	 	 d	 	 	 	 	 	 	 	 	 	 	 ddZ G d	 d
      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Zy)u   tests/test_engine_v2_phase4.py — Phase 4 TDD 테스트 스위트.

G09+G10+G11+G12: ConsensusPipeline, CircuitBreaker, CostTracker 검증.
작성 순서: 테스트 먼저(RED), 구현 후 GREEN 확인.
    )annotationsNz..)Path)	AsyncMockpatchEngineResultc                $    t        || | |||      S )u!   테스트용 EngineResult 생성.)enginecontentcleantask_idsteperrorr   )r   r
   r   r   r   s        J/home/jay/workspace/services/multimodel-bot/tests/test_engine_v2_phase4.py_make_resultr      s#          c                      e Zd ZdZddZy)TestCircuitBreakerClosedu&   CLOSED 상태 기본 동작 테스트.c                    ddl m}m}  |       }|j                  |j                  k(  sJ |j                         du sJ y)u5   CLOSED 상태에서 요청이 허용되어야 한다.r   CBStateCircuitBreakerTN)engine_v2.circuit_breakerr   r   stateCLOSEDallow_requestselfr   r   cbs       r   "test_circuit_breaker_closed_allowsz;TestCircuitBreakerClosed.test_circuit_breaker_closed_allows5   s:    Exx7>>)))!T)))r   NreturnNone)__name__
__module____qualname____doc__r     r   r   r   r   2   s
    0*r   r   c                      e Zd ZdZddZy)TestCircuitBreakerOpenu   OPEN 상태 전환 테스트.c                   ddl m}m}  |d      }|j                  |j                  k(  sJ |j                          |j                  |j                  k(  sJ |j                          |j                  |j                  k(  sJ |j                          |j                  |j                  k(  sJ |j                         du sJ y)u6   3회 실패 후 OPEN 상태로 전환되어야 한다.r   r      )fail_thresholdFN)r   r   r   r   r   record_failureOPENr   r   s       r   *test_circuit_breaker_open_after_3_failureszATestCircuitBreakerOpen.test_circuit_breaker_open_after_3_failuresA   s    E1-xx7>>)))
xx7>>)))
xx7>>)))
xx7<<'''!U***r   Nr!   )r$   r%   r&   r'   r0   r(   r   r   r*   r*   >   s
    '+r   r*   c                  (    e Zd ZdZddZddZddZy)TestCircuitBreakerHalfOpenu"   HALF_OPEN 상태 전환 테스트.c                   ddl m}m}  |dd      }|j                          |j                          |j                          |j                  |j
                  k(  sJ  |dd      }|j                          |j                          |j                          t        j                  d       |j                  |j                  k(  sJ y)u?   recovery_sec 경과 후 HALF_OPEN으로 전환되어야 한다.r   r   r,   <   r-   recovery_sec{Gz?N)
r   r   r   r.   _stater/   timesleepr   	HALF_OPEN)r   r   r   r   cb2s        r   *test_circuit_breaker_recovery_to_half_openzETestCircuitBreakerHalfOpen.test_circuit_breaker_recovery_to_half_openV   s    E12>


yyGLL((( AA>

4yyG-----r   c                ^   ddl m}m}  |dd      }|j                          |j                          |j                          t	        j
                  d       |j                  |j                  k(  sJ |j                          |j                          |j                  |j                  k(  sJ y)uC   HALF_OPEN에서 성공 기록 시 CLOSED로 전환되어야 한다.r   r   r,   r5   r7   N)r   r   r   r.   r9   r:   r   r;   r   record_successr   r   s       r   -test_circuit_breaker_half_open_success_closeszHTestCircuitBreakerHalfOpen.test_circuit_breaker_half_open_success_closesj   s    E11=




4xx7,,,,,

xx7>>)))r   c                :   ddl m}m}  |ddd      }|j                          |j                          |j                          t	        j
                  d       |j                  |j                  k(  sJ |j                         du sJ |j                         du sJ y	)
uM   HALF_OPEN 상태에서 half_max_calls=1이면 1회만 허용되어야 한다.r   r   r,      )r-   r6   half_max_callsr7   TFN)	r   r   r   r.   r9   r:   r   r;   r   r   s       r   (test_circuit_breaker_half_open_max_callszCTestCircuitBreakerHalfOpen.test_circuit_breaker_half_open_max_callsz   s    E11QO




4xx7,,,,, !T)))!U***r   Nr!   )r$   r%   r&   r'   r=   r@   rD   r(   r   r   r2   r2   S   s    ,.(* +r   r2   c                  (    e Zd ZdZddZddZddZy)TestCostTrackerLogUsageu   log_usage() 기록 테스트.c                   ddl m} t        dd      }t        j                  |d|dz        5  |j                  |d	d
       ddd       |dz  }|j                         sJ |j                  d      j                         j                         }t        |      dk(  sJ t        j                  |d         }|d   dk(  sJ |d   d	k(  sJ |d   d
k(  sJ |d   dk(  sJ |d   dk(  sJ y# 1 sw Y   xY w)u4   JSONL 파일에 사용량이 기록되어야 한다.r   cost_trackeru   테스트 결과clauder   r
   _get_log_pathztest_usage.jsonlreturn_valued   gGz?prompt_charsduration_secNzutf-8)encodingrB   r
   rQ   rR   r   task-001r   )	engine_v2rI   r   r   object	log_usageexists	read_textstrip
splitlineslenjsonloads)r   tmp_pathrI   resultlog_filelinesentrys          r   test_cost_tracker_log_usagez3TestCostTrackerLogUsage.test_cost_tracker_log_usage   s   *&8J \\,hQcFcd 	P""6$"O	P 00   ""G"4::<GGI5zQ

58$X(***^$+++^$,,,Y:---V}!!!	P 	Ps   C,,C5c                   ddl m} t        dd      }t        ddd	      }|d
z  }t        j                  |d|      5  |j                  |dd       |j                  |dd       ddd       t        j                  |d|      5  |j                  d      }ddd       t              dk(  sJ |d   d   dk(  sJ |d   d   dk(  sJ y# 1 sw Y   exY w# 1 sw Y   @xY w)u;   기록된 JSONL을 읽어 리스트로 반환해야 한다.r   rH   u   응답1rJ   rK   u   응답2gemini   )r   r
   r   zengine_usage_2026-03.jsonlrL   rM   2   g      ?rP   P   g?N_LOG_DIRz2026-03r
   rB   )rU   rI   r   r   rV   rW   
read_usager\   )r   r_   rI   result1result2ra   entriess          r   test_cost_tracker_read_usagez4TestCostTrackerLogUsage.test_cost_tracker_read_usage   s    *yByJ::\\,hO 	O""7#"N""7#"N	O \\,
H= 	9"--i8G	9 7|q   qz(#x///qz(#x///	O 	O	9 	9s   )CCCC!c                   ddl m }m} ddlm} ddlm} t        d      }|dz  }|dz  }t        j                  |d	|
      5  |j                  |dd       ddd       t        j                  |d	|
      5  |j                  |dd       ddd       |j                         sJ |j                         sJ t        j                  |j                         j                               }	t        j                  |j                         j                               }
|	d   dk(  sJ |
d   dk(  sJ y# 1 sw Y   xY w# 1 sw Y   xY w)u2   월별로 다른 파일에 기록되어야 한다.r   )datetimetimezone)r   rH   u   월별 테스트)r   zengine_usage_2026-01.jsonlzengine_usage_2026-02.jsonlrL   rM   
   g?rP   N   g?rQ   )rq   rr   unittest.mockr   rU   rI   r   rV   rW   rX   r]   r^   rY   rZ   )r   r_   rq   rr   uprI   r`   jan_filefeb_filejan_datafeb_datas              r   test_cost_tracker_monthly_filez6TestCostTrackerLogUsage.test_cost_tracker_monthly_file   s4   /-*&89 ::::\\,hO 	N""6"M	N \\,hO 	N""6"M	N       ::h00288:;::h00288:;'2---'2---	N 	N	N 	Ns   D08D<0D9<EN)r_   r   r"   r#   )r$   r%   r&   r'   rd   ro   r{   r(   r   r   rF   rF      s    '".0,.r   rF   c                      e Zd ZdZddZy)TestConsensusEvaluateHighScoreu3   합의 점수 >= threshold → converged 테스트.c                    ddl m}  |d      }t        dd      t        dd	      g}|j                  |      }|d
u sJ |j                  j
                  du sJ |j                  j                  dk\  sJ y)uV   긍정 시그널만 있으면 합의 점수 >= 0.75이고 converged=True여야 한다.r   ConsensusPipeline      ?	thresholduC   수정 불필요. 충분히 적절하고 우수한 내용입니다.rJ   rK   u"   양호합니다. 수정 불필요.rf   FTNpublishing.consensus_pipeliner   r   should_continuer   	convergedconsensus_scorer   r   pipelineresultsr   s        r   "test_consensus_evaluate_high_scorezATestConsensusEvaluateHighScore.test_consensus_evaluate_high_score   s}    C$t4!fowx!EhW

 #227;%'''~~''4///~~--555r   Nr!   )r$   r%   r&   r'   r   r(   r   r   r}   r}      s
    =6r   r}   c                      e Zd ZdZddZy)TestConsensusEvaluateLowScoreu1   합의 점수 < threshold → continue 테스트.c                    ddl m}  |d      }t        dd      t        dd	      g}|j                  |      }|d
u sJ |j                  j
                  du sJ |j                  j                  dk  sJ y)uT   부정 시그널만 있으면 합의 점수 < 0.75이고 continue=True여야 한다.r   r   r   r   uD   수정 필요. 오류가 있으며 누락된 내용이 많습니다.rJ   rK   uF   부족합니다. major issue가 있고 missing 항목이 있습니다.rf   TFNr   r   s        r   !test_consensus_evaluate_low_scorez?TestConsensusEvaluateLowScore.test_consensus_evaluate_low_score   s~    C$t4!gpxy!irz{

 #227;$&&&~~''5000~~--444r   Nr!   )r$   r%   r&   r'   r   r(   r   r   r   r      s
    ;5r   r   c                      e Zd ZdZddZy)TestConsensusMaxRoundsu)   최대 라운드 강제 종료 테스트.c                   ddl m}m} |dk(  sJ  |d      }t        dd      g}|j	                  |      }|d	u sJ |j
                  j                  d
k(  sJ |j	                  |      }|d	u sJ |j
                  j                  dk(  sJ |j	                  |      }|du sJ |j
                  j                  dk(  sJ |j
                  j                  du sJ y)uF   3라운드 후 강제 종료되어야 한다 (MAX_CONSENSUS_ROUNDS=3).r   )MAX_CONSENSUS_ROUNDSr   r,   r   r   u&   수정 필요. 오류가 있습니다.rJ   rK   TrB   rg   FN)r   r   r   r   r   r   round_numberr   )r   r   r   r   low_score_resultsr1r2r3s           r   test_consensus_max_roundsz0TestConsensusMaxRounds.test_consensus_max_rounds  s    Y#q((($t4!IRZ[

 %%&78Tzz~~**a/// %%&78Tzz~~**a/// %%&78U{{~~**a///~~''5000r   Nr!   )r$   r%   r&   r'   r   r(   r   r   r   r   	  s
    31r   r   c                      e Zd ZdZddZy)!TestConsensusMinorityPreservationu   소수 의견 보존 테스트.c                   ddl m}  |d      }t        dd      t        dd	      g}|j                  |       t	        |j
                  j                        d
k\  sJ t        d |j
                  j                  D              sJ y)uX   반대/우려 키워드가 있는 의견이 minority_opinions에 보존되어야 한다.r   r   r   r   u"   수정 불필요. 우수합니다.rJ   rK   u@   그러나 concern이 있습니다. however 추가 검토 필요.rf   rB   c              3  $   K   | ]  }d |v  
 yw)rf   Nr(   ).0ops     r   	<genexpr>zYTestConsensusMinorityPreservation.test_consensus_minority_preservation.<locals>.<genexpr>7  s     Mb8r>Ms   N)r   r   r   r   r\   r   minority_opinionsany)r   r   r   r   s       r   $test_consensus_minority_preservationzFTestConsensusMinorityPreservation.test_consensus_minority_preservation+  sx    C$t4!EhW!cltu

 	  )8>>334999MHNN,L,LMMMMr   Nr!   )r$   r%   r&   r'   r   r(   r   r   r   r   (  s    )Nr   r   c                  J    e Zd ZdZej
                  j                  dd       Zy)$TestOrchestratorCircuitBreakerBlocksu>   Circuit Breaker OPEN 시 에러 EngineResult 반환 테스트.c                  K   ddl m}m} ddlm}  |       }|j
                  d   j                          |j
                  d   j                          |j
                  d   j                          |j
                  d   j                  |j                  k(  sJ |j                  ddgdgdd	       d
{   }t        |      dk(  sJ |d   j                  du sJ |d   j                  dk(  sJ y
7 <w)uO   CircuitBreaker가 OPEN 상태이면 에러 EngineResult를 반환해야 한다.r   r   )EngineOrchestratorrJ   
SEQUENTIALu   테스트 프롬프트zcb-test-001rB   )modepromptsenginesr   r   NT)r   r   r   engine_v2.engine_orchestratorr   	_breakersr.   r   r/   runr\   r   r
   )r   r   r   r   orchestratorr   s         r   (test_orchestrator_circuit_breaker_blockszMTestOrchestratorCircuitBreakerBlocks.test_orchestrator_circuit_breaker_blocksB  s      	FD)+ 	x(779x(779x(779%%h/55EEE %((-.J! ) 
 
 7|q   qz4'''qz  H,,,
s   B0C12C/3=C1Nr!   )r$   r%   r&   r'   pytestmarkasyncior   r(   r   r   r   r   ?  s!    H[[- -r   r   )u   정상 응답rJ   FrT   rB   )r   strr
   r   r   boolr   r   r   intr"   r   )r'   
__future__r   r]   ossysr9   pathinsertjoindirname__file__pathlibr   ru   r   r   r   engine_v2.engine_resultr   r   r   r*   r2   rF   r}   r   r   r   r   r(   r   r   <module>r      s   #  	 
  277<< 94@ A  *  0 #  	
  .	* 	*+ +*6+ 6+|J. J.d6 6&5 5&1 1>N N.- -r   