
    Uid                    T   d Z ddlm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mZ ddlZddlmZmZmZ ddlmZ dddZddd	Zddd
Z G d d      Z G d d      Z G d d      Zy)u  tests/test_bot_api.py — engine_v2/bot_api.py TDD RED 단계 테스트 스위트.

bot_api.py는 아직 존재하지 않으므로, 이 파일을 실행하면 ImportError로 실패한다.
구현 후 GREEN 단계에서 모든 테스트가 통과해야 한다.

패치 전략:
  - CLIRunner.run_xxx 메서드를 AsyncMock으로 패치하여 CLIResult를 직접 반환한다.
  - subprocess 레벨이 아닌 CLIRunner 레벨에서 모킹하므로 bot_api.py 내부 로직만 검증한다.
    )annotationsNz..)	AsyncMockpatch)call_claude
call_codexcall_gemini	CLIResultc                "    t        ||d| d      S )u1   returncode=0인 정상 CLIResult를 생성한다.r   Fstdoutstderr
returncodeengine	timed_outr	   )r   r   r   s      A/home/jay/workspace/services/multimodel-bot/tests/test_bot_api.py_okr      s         c                "    t        |||| d      S )u-   returncode != 0인 CLIResult를 생성한다.Fr   r	   )r   r   r   r   s       r   _errr   (   s     r   c                *    t        dd| dd| d      S )u,   timed_out=True인 CLIResult를 생성한다. zTimeout after sTr   r	   )r   timeout_secs     r   _timeoutr   3   s'    }A. r   c                     e Zd ZdZej
                  j                  dd       Zej
                  j                  dd       Zej
                  j                  dd       Z	ej
                  j                  dd       Z
ej
                  j                  dd       Zej
                  j                  dd       Zej
                  j                  dd       Zej
                  j                  dd	       Zej
                  j                  dd
       Zej
                  j                  dd       Zej
                  j                  dd       Zy)TestCallClaudeu<   call_claude() — CLIRunner.run_claude 래퍼 동작 검증.c                   K   t        dd      }t        dt        |            5  t        d       d{   }ddd       dk(  sJ y7 # 1 sw Y   xY ww)D   정상 응답 시 CLIResult.stdout을 그대로 반환해야 한다.claudeu!   Claude의 정상 응답입니다.&engine_v2.bot_api.CLIRunner.run_claudereturn_valuenew   테스트 프롬프트Nr   r   r   r   selfresultresponses      r   #test_normal_response_returns_stdoutz2TestCallClaude.test_normal_response_returns_stdoutH   sd      XBC;X^A_` 	C()ABBH	C >>>> C	C 	Cs+   $AAA
AA
AAAc                   K   t        dd      }t        dt        |            5  t        dd       d	{   }d	d	d	       d
k(  sJ y	7 # 1 sw Y   xY ww)o   timed_out=True인 CLIResult가 반환되면 '⏱ 응답 시간 초과 (N초)' 메시지를 반환해야 한다.r!   X  r   r"   r#   r%      타임아웃 테스트timeoutN!   ⏱ 응답 시간 초과 (600초)r   r   r   r   r)   s      r   $test_timeout_returns_timeout_messagez3TestCallClaude.test_timeout_returns_timeout_messageT   e      (4;X^A_` 	P()A3OOH	P >>>> P	P 	P+   %AAAAAAAAc                   K   t        dd      }t        dt        |            5  t        dd       d	{   }d	d	d	       d
k(  sJ y	7 # 1 sw Y   xY ww)M   timeout 파라미터 값이 타임아웃 메시지에 반영되어야 한다.r!      r1   r"   r#   r%      커스텀 타임아웃r3   Nu    ⏱ 응답 시간 초과 (30초)r6   r)   s      r   ,test_timeout_message_reflects_custom_timeoutz;TestCallClaude.test_timeout_message_reflects_custom_timeout^   e      (3;X^A_` 	O()A2NNH	O ==== O	O 	Or9   c                   K   d}t        dd|d      }t        dt        |            5  t        d	       d
{   }d
d
d
       dv sJ d|vsJ d|vsJ d|v sJ y
7 %# 1 sw Y   $xY ww)ur   returncode != 0이고 stdout이 없을 때 stderr에서 'error' 포함 줄만 필터링하여 반환해야 한다.z8info: starting
Error: authentication failed
debug: done
r!   r      r   r   r   r"   r#   r%      에러 필터링 테스트NzError: authentication failedzinfo: startingzdebug: doneu   ❌ Claude CLI 에러 (exit 1):r   r   r   r   r*   r   r+   r,   s       r   5test_nonzero_returncode_no_stdout_filters_error_lineszDTestCallClaude.test_nonzero_returncode_no_stdout_filters_error_linesj   s      Ohr&QG;X^A_` 	G()EFFH	G .999x///H,,,0H<<< G	G 	Gs+   )A.A"A A"!A. A""A+'A.c                   K   d}t        dd|d      }t        dt        |            5  t        d	       d
{   }d
d
d
       dv sJ d|v sJ y
7 # 1 sw Y   xY ww)ux   returncode != 0이고 stdout이 없고 stderr에 'error' 줄도 없으면 '상세 내용 없음'을 반환해야 한다.z%warning: something odd
info: exiting
r!   r      rB   r"   r#   r%      error 줄 없음 테스트Nu   ❌ Claude CLI 에러 (exit 2):   상세 내용 없음rD   rE   s       r   @test_nonzero_returncode_no_stdout_no_error_lines_returns_genericzOTestCallClaude.test_nonzero_returncode_no_stdout_no_error_lines_returns_genericx   sy      ;hr&QG;X^A_` 	G()EFFH	G 1H<<<%111 G	G 	Gs+   )A"AAAA"AAA"c                   K   t        dddd      }t        dt        |            5  t        d	       d
{   }d
d
d
       dk(  sJ y
7 # 1 sw Y   xY ww)uo   returncode != 0이더라도 stdout에 출력이 있으면 에러를 무시하고 stdout을 반환해야 한다.r!   zpartial output from claudezError: some stderrrA   rB   r"   r#   r%   u   출력 있는 에러 테스트NrD   r)   s      r   2test_nonzero_returncode_with_stdout_returns_stdoutzATestCallClaude.test_nonzero_returncode_with_stdout_returns_stdout   sj      h'CL`mno;X^A_` 	K()IJJH	K 7777 K	K 	Ks+   'AAAAAAAAc                   K   t        dd      }t        dt        |            5  t        d       d{   }ddd       d	k(  sJ y7 # 1 sw Y   xY ww)
Y   returncode=0이고 stdout이 빈 문자열이면 경고 메시지를 반환해야 한다.r!   r   r   r"   r#   r%      빈 응답 테스트Nu<   ⚠️ Claude CLI에서 빈 응답이 반환되었습니다.r(   r)   s      r   0test_empty_stdout_returns_empty_response_messagez?TestCallClaude.test_empty_stdout_returns_empty_response_message   sc      Xb);X^A_` 	A()?@@H	A YYYY A	A 	A+   %AAAAAAAAc                   K   t        dd      }t        |      }t        d|      5  t        d       d{    ddd       |j                  \  }}|j                  d	      d
u sJ y7 1# 1 sw Y   0xY ww)u[   code_analysis=False(기본값)이 CLIRunner.run_claude에 그대로 전달되어야 한다.r!      응답rP   r#   r"   r%   u   일반 질문Ncode_analysisFr   r   r   r   	call_argsgetr*   r+   mock_run_kwargss        r   )test_code_analysis_false_passed_to_runnerz8TestCallClaude.test_code_analysis_false_passed_to_runner   sy      Xh/&1;J 	/o...	/ &&	6zz/*e333 /	/ 	/s+   'A8A,A*A,-A8*A,,A51A8c                   K   t        dd      }t        |      }t        d|      5  t        dd	       d
{    d
d
d
       |j                  \  }}|j                  d      du sJ y
7 1# 1 sw Y   0xY ww)uO   code_analysis=True가 CLIRunner.run_claude에 그대로 전달되어야 한다.r!   u   코드 분석 결과rP   r#   r"   r%   u   코드 분석 요청T)rV   NrV   rW   rZ   s        r   (test_code_analysis_true_passed_to_runnerz7TestCallClaude.test_code_analysis_true_passed_to_runner   s      X&<=&1;J 	J4DIII	J &&	6zz/*d222 J	J 	Js+   'A:A.A,A.-A:,A..A73A:c                   K   t        dd      }t        |      }t        d|      5  t        dd	       d
{    d
d
d
       |j                  \  }}|j                  d      dk(  sJ y
7 2# 1 sw Y   1xY ww)uG   timeout 파라미터가 CLIRunner.run_claude에 전달되어야 한다.r!   rU   rP   r#   r"   r%   u   타임아웃 값 확인x   r3   Nr4   rW   rZ   s        r   #test_timeout_param_passed_to_runnerz2TestCallClaude.test_timeout_param_passed_to_runner   s      Xh/&1;J 	F7EEE	F &&	6zz)$+++ F	F 	F+   'A;A/A-A/.A;-A//A84A;c                   K   t        dd      }t        |      }d}t        d|      5  t        |       d{    ddd       |j                  \  }}|d	   |k(  sJ y7 &# 1 sw Y   %xY ww)
u^   프롬프트 문자열이 CLIRunner.run_claude에 첫 번째 인자로 전달되어야 한다.r!   rU   rP   r#   *   이 프롬프트가 전달되어야 한다r"   r%   Nr   )r   r   r   r   rX   r*   r+   r[   promptargsr\   s         r   test_prompt_passed_to_runnerz+TestCallClaude.test_prompt_passed_to_runner   sy      Xh/&1=;J 	&f%%%	& $$aAw&    &	& 	&+   )A/A#A!A#"A/!A##A,(A/NreturnNone)__name__
__module____qualname____doc__pytestmarkasyncior-   r7   r>   rF   rK   rM   rR   r^   r`   rc   rj    r   r   r   r   C   sQ   F [[? ? [[? ? [[> > [[= = [[	2 	2 [[8 8 [[Z Z [[	4 	4 [[	3 	3 [[	, 	, [[
! 
!r   r   c                  v   e Zd ZdZej
                  j                  dd       Zej
                  j                  dd       Zej
                  j                  dd       Z	ej
                  j                  dd       Z
ej
                  j                  dd       Zej
                  j                  dd       Zej
                  j                  dd       Zej
                  j                  dd	       Zej
                  j                  dd
       Zej
                  j                  dd       Zej
                  j                  dd       Zej
                  j                  dd       Zej
                  j                  dd       Zej
                  j                  dd       Zej
                  j                  dd       Zy)TestCallCodexu:   call_codex() — CLIRunner.run_codex 래퍼 동작 검증.c                   K   t        dd      }t        dt        |            5  t        d       d{   }ddd       dk(  sJ y7 # 1 sw Y   xY ww)	r    codexu    Codex의 정상 응답입니다.rP   %engine_v2.bot_api.CLIRunner.run_codexr#   r%   u   코드 리뷰 요청Nr   r   r   r   r)   s      r   r-   z1TestCallCodex.test_normal_response_returns_stdout   sd      W%GH:	W]@^_ 	@'(>??H	@ ==== @	@ 	@rS   c                   K   t        dd      }t        dt        |            5  t        dd       d	{   }d	d	d	       d
k(  sJ y	7 # 1 sw Y   xY ww)r/   rz   r0   r1   r{   r#   r%   r2   r3   Nr5   r   r   r   r   r)   s      r   r7   z2TestCallCodex.test_timeout_returns_timeout_message   se      's3:	W]@^_ 	O'(@#NNH	O >>>> O	O 	Or9   c                   K   t        dd      }t        dt        |            5  t        dd       d	{   }d	d	d	       d
k(  sJ y	7 # 1 sw Y   xY ww)r;   rz   -   r1   r{   r#   r%   r=   r3   Nu    ⏱ 응답 시간 초과 (45초)r~   r)   s      r   r>   z:TestCallCodex.test_timeout_message_reflects_custom_timeout   se      'r2:	W]@^_ 	N'(@"MMH	N ==== N	N 	Nr9   c                   K   t        ddd      }t        dt        |            5  t        d       d	{   }d	d	d	       d
k(  sJ y	7 # 1 sw Y   xY ww)uj   returncode != 0이고 stderr에 'login'이 포함되면 로그인 안내 메시지를 반환해야 한다.rz   z3Error: Not authenticated. Please run `codex login`.rA   r   r   r{   r#   r%   u   로그인 없이 요청NM   🔑 Codex 로그인이 필요합니다. `codex login`을 실행해주세요.r   r   r   r   r)   s      r   <test_nonzero_returncode_login_in_stderr_returns_auth_messagezJTestCallCodex.test_nonzero_returncode_login_in_stderr_returns_auth_message   sg      g&[hij:	W]@^_ 	C'(ABBH	C jjjj C	C 	C+   &AAAAAAAAc                   K   t        ddd      }t        dt        |            5  t        d       d	{   }d	d	d	       d
k(  sJ y	7 # 1 sw Y   xY ww)ui   returncode != 0이고 stderr에 'auth'가 포함되면 로그인 안내 메시지를 반환해야 한다.rz   zauthentication requiredrA   r   r{   r#   r%   u   auth 에러 테스트Nr   r   r)   s      r   ;test_nonzero_returncode_auth_in_stderr_returns_auth_messagezITestCallCodex.test_nonzero_returncode_auth_in_stderr_returns_auth_message  sf      g&?AN:	W]@^_ 	A'(?@@H	A jjjj A	A 	Ar   c                   K   t        ddd      }t        dt        |            5  t        d       d	{   }d	d	d	       d
k(  sJ y	7 # 1 sw Y   xY ww)uw   returncode != 0이고 stderr에 'usage limit'이 포함되면 사용량 한도 초과 메시지를 반환해야 한다.rz   z)Error: You have reached your usage limit.rA   r   r{   r#   r%   u   사용량 한도 테스트NK   ⚠️ Codex 사용량 한도 초과. 잠시 후 다시 시도해주세요.r   r)   s      r   Ctest_nonzero_returncode_usage_limit_in_stderr_returns_limit_messagezQTestCallCodex.test_nonzero_returncode_usage_limit_in_stderr_returns_limit_message  sg      g&Q^_`:	W]@^_ 	F'(DEEH	F hhhh F	F 	Fr   c                   K   t        ddd      }t        dt        |            5  t        d       d	{   }d	d	d	       d
k(  sJ y	7 # 1 sw Y   xY ww)ut   returncode != 0이고 stderr에 'hit your'가 포함되면 사용량 한도 초과 메시지를 반환해야 한다.rz   z You have hit your monthly quota.rA   r   r{   r#   r%   u   hit your 에러 테스트Nr   r   r)   s      r   @test_nonzero_returncode_hit_your_in_stderr_returns_limit_messagezNTestCallCodex.test_nonzero_returncode_hit_your_in_stderr_returns_limit_message  sg      g&HUVW:	W]@^_ 	E'(CDDH	E hhhh E	E 	Er   c                   K   d}t        d|d      }t        dt        |            5  t        d       d	{   }d	d	d	       d
v sJ d|v sJ d|vsJ d|vsJ y	7 %# 1 sw Y   $xY ww)ud   returncode != 0이고 stderr에 'error' 줄이 있으면 필터링된 줄만 반환되어야 한다.zAinfo: starting process
Error: connection refused
debug: retrying
rz   rA   r   r{   r#   r%   rC   NzError: connection refused   ❌ Codex CLI 에러 (exit 1):zinfo: starting processzdebug: retryingr   rE   s       r   9test_nonzero_returncode_error_lines_filtered_and_returnedzGTestCallCodex.test_nonzero_returncode_error_lines_filtered_and_returned&  s      Xgf;:	W]@^_ 	F'(DEEH	F +h666/8;;;'x777 000 F	F 	Fs+   (A-A!AA!!A-A!!A*&A-c                   K   d}t        d|d      }t        dt        |            5  t        d       d	{   }d	d	d	       d
v sJ d|v sJ y	7 # 1 sw Y   xY ww)ug   returncode != 0이고 stderr에 'error' 줄이 없으면 '상세 내용 없음'을 반환해야 한다.z1warning: something happened
info: process exited
rz   rA   r   r{   r#   r%   rI   Nr   rJ   r   rE   s       r   >test_nonzero_returncode_no_error_lines_returns_generic_messagezLTestCallCodex.test_nonzero_returncode_no_error_lines_returns_generic_message4  sx      Ggf;:	W]@^_ 	F'(DEEH	F 08;;;%111 F	F 	F+   (A!AAAA!AAA!c                   K   d}t        d|d      }t        dt        |            5  t        d       d	{   }d	d	d	       d
}t	        |      d	 }t	        |      dk  sJ y	7 -# 1 sw Y   ,xY ww)uF   필터링된 에러 내용이 300자를 초과하면 잘려야 한다.a  Error: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxrz   rA   r   r{   r#   r%   u   300자 제한 테스트Nu   ❌ Codex CLI 에러 (exit 1): i,  )r   r   r   r   len)r*   
long_errorr+   r,   prefix
error_parts         r   )test_error_filtered_content_max_300_charsz7TestCallCodex.test_error_filtered_content_max_300_chars@  s      +
gjQ?:	W]@^_ 	C'(ABBH	C 3c&km,
:#%%%	 C	C 	Cs+   (A5A)A'A))A5'A))A2.A5c                   K   t        dd      }t        dt        |            5  t        d       d{   }ddd       d	k(  sJ y7 # 1 sw Y   xY ww)
rO   rz   r   rP   r{   r#   r%   rQ   Nu;   ⚠️ Codex CLI에서 빈 응답이 반환되었습니다.r|   r)   s      r   rR   z>TestCallCodex.test_empty_stdout_returns_empty_response_messageO  sc      WR(:	W]@^_ 	@'(>??H	@ XXXX @	@ 	@rS   c                   K   t        dd      }t        |      }t        d|      5  t        d       d{    ddd       |j                  \  }}|j                  d	      d
k(  sJ y7 2# 1 sw Y   1xY ww)um   model 파라미터 없이 호출하면 기본값 'gpt-5.1-codex-mini'가 CLIRunner에 전달되어야 한다.rz   okrP   r#   r{   r%   u   기본 모델 테스트Nmodelzgpt-5.1-codex-minir   r   r   r   rX   rY   rZ   s        r   #test_default_model_passed_to_runnerz1TestCallCodex.test_default_model_passed_to_runner[  s{      WT*&1:I 	86777	8 &&	6zz'"&:::: 8	8 	8s+   'A9A-A+A-.A9+A--A62A9c                   K   t        dd      }t        |      }t        d|      5  t        dd	       d
{    d
d
d
       |j                  \  }}|j                  d      dk(  sJ y
7 2# 1 sw Y   1xY ww)uN   model 파라미터가 CLIRunner.run_codex에 그대로 전달되어야 한다.rz   r   rP   r#   r{   r%   u   커스텀 모델 테스트zgpt-5.2-codex)r   Nr   r   rZ   s        r   "test_custom_model_passed_to_runnerz0TestCallCodex.test_custom_model_passed_to_runnerg  s      WT*&1:I 	R9QQQ	R &&	6zz'"o555 R	R 	Rrd   c                   K   t        dd      }t        |      }t        d|      5  t        dd	       d
{    d
d
d
       |j                  \  }}|j                  d      dk(  sJ y
7 2# 1 sw Y   1xY ww)uF   timeout 파라미터가 CLIRunner.run_codex에 전달되어야 한다.rz   r   rP   r#   r{   r%      타임아웃 전달 테스트Z   r3   Nr4   r   rZ   s        r   rc   z1TestCallCodex.test_timeout_param_passed_to_runners  s      WT*&1:I 	J<bIII	J &&	6zz)$*** J	J 	Jrd   c                   K   t        dd      }t        |      }d}t        d|      5  t        |       d{    ddd       |j                  \  }}|d	   |k(  sJ y7 &# 1 sw Y   %xY ww)
u]   프롬프트 문자열이 CLIRunner.run_codex에 첫 번째 인자로 전달되어야 한다.rz   r   rP   r#   rf   r{   r%   Nr   )r   r   r   r   rX   rg   s         r   rj   z*TestCallCodex.test_prompt_passed_to_runner  sy      WT*&1=:I 	%V$$$	% $$aAw&    %	% 	%rk   Nrl   )ro   rp   rq   rr   rs   rt   ru   r-   r7   r>   r   r   r   r   r   r   r   rR   r   r   rc   rj   rv   r   r   rx   rx      s   D [[> > [[? ? [[> > [[k k [[k k [[i i [[i i [[1 1 [[	2 	2 [[
& 
& [[Y Y [[	; 	; [[	6 	6 [[	+ 	+ [[
! 
!r   rx   c                  T   e Zd ZdZej
                  j                  dd       Zej
                  j                  dd       Zej
                  j                  dd       Z	ej
                  j                  dd       Z
ej
                  j                  dd       Zej
                  j                  dd       Zej
                  j                  dd       Zej
                  j                  dd	       Zej
                  j                  dd
       Zej
                  j                  dd       Zy)TestCallGeminiu<   call_gemini() — CLIRunner.run_gemini 래퍼 동작 검증.c                   K   t        dd      }t        dt        |            5  t        d       d{   }ddd       dk(  sJ y7 # 1 sw Y   xY ww)	r    geminiu!   Gemini의 정상 응답입니다.rP   &engine_v2.bot_api.CLIRunner.run_geminir#   r%   r'   Nr   r   r   r   r)   s      r   r-   z2TestCallGemini.test_normal_response_returns_stdout  sd      X&IJ;X^A_` 	C()ABBH	C >>>> C	C 	CrS   c                   K   t        dd      }t        dt        |            5  t        dd       d	{   }d	d	d	       d
k(  sJ y	7 # 1 sw Y   xY ww)r/   r   r0   r1   r   r#   r%   r2   r3   Nr5   r   r   r   r   r)   s      r   r7   z3TestCallGemini.test_timeout_returns_timeout_message  r8   r9   c                   K   t        dd      }t        dt        |            5  t        dd       d	{   }d	d	d	       d
k(  sJ y	7 # 1 sw Y   xY ww)r;   r   <   r1   r   r#   r%   r=   r3   Nu    ⏱ 응답 시간 초과 (60초)r   r)   s      r   r>   z;TestCallGemini.test_timeout_message_reflects_custom_timeout  r?   r9   c                   K   d}t        d|d      }t        dt        |            5  t        d       d	{   }d	d	d	       d
v sJ ||v sJ y	7 # 1 sw Y   xY ww)uY   returncode != 0이면 stderr 내용을 포함한 에러 메시지를 반환해야 한다.z*gemini: command failed: API quota exceededr   rA   r   r   r#   r%   u   Gemini 에러 테스트Nu   ❌ Gemini CLI 에러 (exit 1):r   r   r   r   rE   s       r   1test_nonzero_returncode_returns_error_with_stderrz@TestCallGemini.test_nonzero_returncode_returns_error_with_stderr  sw      >hv!<;X^A_` 	D()BCCH	D 1H<<<!!! D	D 	Dr   c                   K   t        ddd      }t        dt        |            5  t        d       d	{   }d	d	d	       d
v sJ y	7 # 1 sw Y   xY ww)u?   에러 메시지에 실제 exit code가 포함되어야 한다.r   z
some errorrH   r   r   r#   r%   u   exit code 확인Nzexit 2r   r)   s      r   ,test_nonzero_returncode_exit_code_in_messagez;TestCallGemini.test_nonzero_returncode_exit_code_in_message  s_      h|B;X^A_` 	=();<<H	= 8### =	= 	=+   &AAAAAAAAc                   K   t        ddd      }t        dt        |            5  t        d       d	{   }d	d	d	       d
v sJ y	7 # 1 sw Y   xY ww)u`   returncode != 0이고 stderr가 비어 있어도 에러 메시지 형식을 반환해야 한다.r   r      r   r   r#   r%   u   빈 stderr 에러Nu   ❌ Gemini CLI 에러 (exit 3):r   r)   s      r   :test_nonzero_returncode_empty_stderr_returns_error_messagezITestCallGemini.test_nonzero_returncode_empty_stderr_returns_error_message  s_      hra8;X^A_` 	>()<==H	> 1H<<< >	> 	>r   c                   K   t        dd      }t        dt        |            5  t        d       d{   }ddd       d	k(  sJ y7 # 1 sw Y   xY ww)
rO   r   r   rP   r   r#   r%   rQ   Nu8   ⚠️ Gemini에서 빈 응답이 반환되었습니다.r   r)   s      r   rR   z?TestCallGemini.test_empty_stdout_returns_empty_response_message  sc      Xb);X^A_` 	A()?@@H	A UUUU A	A 	ArS   c                   K   d}t        d|d      }t        dt        |            5  t        d       d	{   }d	d	d	       |v sJ y	7 # 1 sw Y   xY ww)
uN   Gemini 에러는 stderr 필터링 없이 내용 전체를 반환해야 한다.z<warning: some warning
info: detail info
fatal error occurredr   rA   r   r   r#   r%   u   stderr 전체 포함 테스트Nr   rE   s       r   'test_error_includes_full_stderr_contentz6TestCallGemini.test_error_includes_full_stderr_content  sj      Rhv!<;X^A_` 	K()IJJH	K !!! K	K 	Ks+   (AAAAAAAAc                   K   t        dd      }t        |      }t        d|      5  t        dd	       d
{    d
d
d
       |j                  \  }}|j                  d      dk(  sJ y
7 2# 1 sw Y   1xY ww)uG   timeout 파라미터가 CLIRunner.run_gemini에 전달되어야 한다.r   r   rP   r#   r   r%   r      r3   Nr4   )r   r   r   r   rX   rY   rZ   s        r   rc   z2TestCallGemini.test_timeout_param_passed_to_runner  s      Xd+&1;J 	L=sKKK	L &&	6zz)$+++ L	L 	Lrd   c                   K   t        dd      }t        |      }d}t        d|      5  t        |       d{    ddd       |j                  \  }}|d	   |k(  sJ y7 &# 1 sw Y   %xY ww)
u^   프롬프트 문자열이 CLIRunner.run_gemini에 첫 번째 인자로 전달되어야 한다.r   r   rP   r#   u#   Gemini에게 전달할 프롬프트r   r%   Nr   )r   r   r   r   rX   rg   s         r   rj   z+TestCallGemini.test_prompt_passed_to_runner  sy      Xd+&16;J 	&f%%%	& $$aAw&    &	& 	&rk   Nrl   )ro   rp   rq   rr   rs   rt   ru   r-   r7   r>   r   r   r   rR   r   rc   rj   rv   r   r   r   r     s3   F [[? ? [[? ? [[> > [[	" 	" [[$ $ [[= = [[V V [[	" 	" [[	, 	, [[
! 
!r   r   )r   )r   strr   r   r   r   rm   r
   )r   r   rA   )
r   r   r   r   r   r   r   intrm   r
   )r0   )r   r   r   r   rm   r
   )rr   
__future__r   ossyspathinsertjoindirname__file__unittest.mockr   r   rs   engine_v2.bot_apir   r   r   engine_v2.cli_runnerr
   r   r   r   r   rx   r   rv   r   r   <module>r      s    # 	 
 277<< 94@ A *  B B * H! H!`w! w!~x! x!r   