
    i`                       d Z ddlmZ ddlZddlZddlZddlZddlmZ ddl	m
Z
mZ ddlZ	 ddlmZ  ej                   dd      Zej                  j%                  e      Zej(                  j+                  e       dZej4                  j7                  edud	e 
      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"y# e$ rZ ej2                  d      ZeZY dZ[xdZ[ww xY w)u  
glm-call.py 유닛 테스트

테스트 범위:
  - _parse_env_keys_file: .env 형식 파싱
  - load_api_key: 환경변수 우선순위 및 .env.keys 폴백
  - call_api: HTTP POST 호출, 재시도, 오류 처리
  - build_parser: argparse 구성 확인
  - main: CLI 통합 흐름

외부 의존성:
  - requests.post → unittest.mock.patch 로 모킹
  - time.sleep → unittest.mock.patch 로 모킹 (테스트 속도 보장)
  - 파일 I/O → pytest tmp_path fixture 사용
    )annotationsN)Path)	MagicMockpatchglm_callz%/home/jay/workspace/tools/glm-call.pyglm_call_stubu   glm-call.py 임포트 실패: )reasonc                n    t               }d|j                  _        ddd| digi|j                  _        |S )u'   정상 응답 mock 을 반환합니다.Nchoicesmessage	assistantrolecontent)r   raise_for_statusreturn_valuejson)r   	mock_resps     J/home/jay/workspace/.worktrees/task-2116-dev1/tools/tests/test_glm_call.py_ok_responser   8   s<    I.2I+#,	K\c;d/e.f"gINN    c                  `    e Zd ZdZddZddZddZddZddZddZ	ddZ
dd	Zdd
ZddZy)TestParseEnvKeysFileu%   _parse_env_keys_file 단위 테스트c                    |dz  }|j                  dd       t        j                  t        |            }|ddik(  sJ y)u0   KEY="value" 형식을 올바르게 파싱한다.	.env.keyszKEY="value"
utf-8encodingKEYvalueN
write_textr   _parse_env_keys_filestrselftmp_pathenv_fileresults       r   test_basic_key_valuez)TestParseEnvKeysFile.test_basic_key_valueH   sF    k)Og>..s8}=%))))r   c                    |dz  }|j                  dd       t        j                  t        |            }|ddik(  sJ y)u?   export KEY=value 형식에서 export 접두어를 제거한다.r   zexport MY_KEY=my_value
r   r   MY_KEYmy_valueNr!   r%   s       r   test_export_prefixz'TestParseEnvKeysFile.test_export_prefixQ   sG    k)6I..s8}=(J////r   c                    |dz  }|j                  dd       t        j                  t        |            }d|v sJ t	        |      dk(  sJ y)u9   # 로 시작하는 주석 줄과 빈 줄은 무시한다.r   u-   # 이것은 주석입니다

VALID_KEY=hello
r   r   	VALID_KEY   N)r"   r   r#   r$   lenr%   s       r   test_comment_lines_ignoredz/TestParseEnvKeysFile.test_comment_lines_ignoredZ   sU    k)NY`a..s8}=f$$$6{ar   c                    |dz  }|j                  dd       t        j                  t        |            }|d   dk(  sJ y)u8   큰따옴표로 감싼 값의 따옴표를 제거한다.r   zTOKEN="abc123"
r   r   TOKENabc123Nr!   r%   s       r   test_double_quote_removalz.TestParseEnvKeysFile.test_double_quote_removald   F    k).A..s8}=g(***r   c                    |dz  }|j                  dd       t        j                  t        |            }|d   dk(  sJ y)u;   작은따옴표로 감싼 값의 따옴표를 제거한다.r   zTOKEN='abc123'
r   r   r5   r6   Nr!   r%   s       r   test_single_quote_removalz.TestParseEnvKeysFile.test_single_quote_removalm   r8   r   c                <    t         j                  d      }|i k(  sJ y)u;   파일이 존재하지 않으면 빈 dict 를 반환한다.z/nonexistent/path/.env.keysN)r   r#   r&   r)   s     r   &test_file_not_found_returns_empty_dictz;TestParseEnvKeysFile.test_file_not_found_returns_empty_dictv   s    ../LM||r   c                    |dz  }|j                  dd       t        j                  t        |            }d|vsJ |j	                  d      dk(  sJ y)	u!   = 가 없는 줄은 무시한다.r   zINVALID_LINE
VALID=ok
r   r   INVALID_LINEVALIDokN)r"   r   r#   r$   getr%   s       r    test_line_without_equals_ignoredz5TestParseEnvKeysFile.test_line_without_equals_ignored|   sY    k)6I..s8}=V+++zz'"d***r   c                    |dz  }|j                  dd       t        j                  t        |            }|d   dk(  sJ |d   dk(  sJ y	)
u%   여러 키를 동시에 파싱한다.r   z1GLM_API_KEY="key-xxx"
export OTHER_KEY=other_val
r   r   GLM_API_KEYzkey-xxx	OTHER_KEY	other_valNr!   r%   s       r   test_multiple_keysz'TestParseEnvKeysFile.test_multiple_keys   sa    k)A 	 	

 ..s8}=m$	111k"k111r   c                    |dz  }|j                  dd       t        j                  t        |            }|d   dk(  sJ y)uM   값 안에 = 가 포함된 경우 첫 번째 = 를 기준으로 분리한다.r   zTOKEN=abc=def=ghi
r   r   r5   zabc=def=ghiNr!   r%   s       r   test_value_with_equals_signz0TestParseEnvKeysFile.test_value_with_equals_sign   sF    k)1GD..s8}=g-///r   c                ~    |dz  }|j                  dd       t        j                  t        |            }|i k(  sJ y)u(   빈 파일은 빈 dict 를 반환한다.r    r   r   Nr!   r%   s       r   test_empty_filez$TestParseEnvKeysFile.test_empty_file   s?    k)B1..s8}=||r   Nr'   r   returnNonerO   rP   )__name__
__module____qualname____doc__r*   r.   r3   r7   r:   r=   rC   rH   rJ   rM    r   r   r   r   E   s7    /*0 +++20r   r   c                  8    e Zd ZdZddZd	dZddZddZddZy)
TestLoadApiKeyu   load_api_key 단위 테스트c                6   |dz  }|j                  dd       t        j                  dddi      5  t        j                  t        dt        |            5  t        j                         }d	d	d	       d	d	d	       dk(  sJ y	# 1 sw Y   xY w# 1 sw Y   xY w)
u4   환경변수가 .env.keys 파일보다 우선한다.r   zGLM_API_KEY="file-key"
r   r   
os.environrE   zenv-keyENV_KEYS_PATHNr"   r   dictobjectr   r$   load_api_keyr%   s       r   %test_env_var_takes_priority_over_filez4TestLoadApiKey.test_env_var_takes_priority_over_file   s    k)6IZZ}i&@A 	1hXG 1!..01	1 """1 1	1 	1#   %BB+BB	BBc                    t        j                  dddi      5  t        j                         }ddd       dk(  sJ y# 1 sw Y   xY w)u;   환경변수만 존재할 때 정상적으로 반환한다.rZ   rE   z
my-api-keyN)r   r]   r   r_   r<   s     r   test_env_var_onlyz TestLoadApiKey.test_env_var_only   sH    ZZ}l&CD 	-**,F	- %%%	- 	-s	   >Ac                   |dz  }|j                  dd       t        d      j                  j                         D ci c]  \  }}|dk7  s|| }}}t	        j
                  d|d	      5  t	        j                  t        d
t        |            5  t        j                         }ddd       ddd       dk(  sJ yc c}}w # 1 sw Y   xY w# 1 sw Y   #xY w)uC   환경변수가 없을 때 .env.keys 파일에서 키를 읽는다.r   zGLM_API_KEY="file-api-key"
r   r   osrE   rZ   Tclearr[   Nzfile-api-key)
r"   
__import__environitemsr   r]   r^   r   r$   r_   )r&   r'   r(   kvenv_without_keyr)   s          r   test_fallback_to_env_keys_filez-TestLoadApiKey.test_fallback_to_env_keys_file   s    k):WM,6t,<,D,D,J,J,LcDAqPQUbPb1a4ccZZoTB 	1hXG 1!..01	1 ''' d1 1	1 	1s/   B?B?-%CC'CC	
CCc                   t        |dz        }t        d      j                  j                         D ci c]  \  }}|dk7  s|| }}}t	        j
                  d|d      5  t	        j                  t        d|      5  t        j                  t              5 }t        j                          ddd       ddd       ddd       j                  j                  d	k(  sJ yc c}}w # 1 sw Y   ;xY w# 1 sw Y   ?xY w# 1 sw Y   CxY w)
uH   환경변수도 없고 파일도 없으면 sys.exit(1) 을 호출한다.znonexistent.keysre   rE   rZ   Trf   r[   Nr1   )r$   rh   ri   rj   r   r]   r^   r   pytestraises
SystemExitr_   r    code)r&   r'   missing_pathrk   rl   rm   exc_infos          r   test_no_key_anywhere_exitsz)TestLoadApiKey.test_no_key_anywhere_exits   s    8&889,6t,<,D,D,J,J,LcDAqPQUbPb1a4ccZZoTB 	,hF ,]]:. ,())+,,	,
 ~~""a''' d, ,, ,	, 	,sG   C"C"#D ?C4C(.C46D (C1-C44C=	9D  D	c                6   |dz  }|j                  dd       t        j                  dddi      5  t        j                  t        dt        |            5  t        j                         }d	d	d	       d	d	d	       d
k(  sJ y	# 1 sw Y   xY w# 1 sw Y   xY w)uB   환경변수가 빈 문자열이면 파일 폴백을 사용한다.r   zGLM_API_KEY="from-file"
r   r   rZ   rE   rL   r[   Nz	from-filer\   r%   s       r   %test_empty_env_var_falls_back_to_filez4TestLoadApiKey.test_empty_env_var_falls_back_to_file   s    k)7'JZZ}b&9: 	1hXG 1!..01	1 $$$1 1	1 	1ra   NrN   rQ   )	rR   rS   rT   rU   r`   rc   rn   rv   rx   rV   r   r   rX   rX      s    '	#&
(
(	%r   rX   c                      e Zd ZdZ eddddd      ZddZdd	Zdd
ZddZ	ddZ
ddZddZddZddZddZddZy)TestCallApiu0   call_api 단위 테스트 (requests.post 모킹)test-keyglm-5systemhellod   )api_keymodelsystem_promptuser_message
max_tokensc                    t        dt        d            5 }t        j                  di | j                  }ddd       dk(  sJ j                          y# 1 sw Y   !xY w)u7   정상 응답에서 assistant content 를 반환한다.requests.postu   정상 응답r   NrV   )r   r   r   call_api	_DEFAULTSassert_called_once)r&   	mock_postr)   s      r   test_successful_responsez$TestCallApi.test_successful_response   s\    ?o1NO 	9S\&&88F	9 ((($$&		9 	9s    AA c                >   t        dt                     5 }t        j                  di | j                   ddd       j
                  }|j                  j                  d      xs |d   j                  di       }|j                  d      dk(  sJ y# 1 sw Y   ^xY w)	u6   Authorization 헤더에 Bearer 토큰이 포함된다.r   r   Nheadersr1   AuthorizationzBearer test-keyrV   )r   r   r   r   r   	call_argskwargsrB   )r&   r   call_kwargsr   s       r   *test_request_includes_authorization_headerz6TestCallApi.test_request_includes_authorization_header   s    ?@ 	0I//	0  ))$$((3X{1~7I7I)UW7X{{?+/@@@@	0 	0s    BBc                   ddl }|j                  j                  d      gt        j                  dz   z  }t        d|      5  t        d      5  t        j                  t              5 }t        j                  di | j                   ddd       ddd       ddd       j                  j                  dk(  sJ y# 1 sw Y   5xY w# 1 sw Y   9xY w# 1 sw Y   =xY w)	uH   타임아웃 발생 시 MAX_RETRIES 만큼 재시도 후 exit(1) 한다.r   N	timed outr1   r   side_effect
time.sleeprV   )requests
exceptionsTimeoutr   MAX_RETRIESr   rp   rq   rr   r   r   r    rs   )r&   _reqside_effectsru   s       r   test_timeout_triggers_retryz'TestCallApi.test_timeout_triggers_retry  s    //<=AUAUXYAYZ?= 	8|$ 8]]:. 8(%%7788	8
 ~~""a'''8 88 8	8 	8s<   CC( B<CC<CCC	CCc                   ddl }|j                  j                  d      gt        j                  dz   z  }t        d|      5 }t        d      5  t        j                  t              5  t        j                  di | j                   ddd       ddd       ddd       j                  t        j                  dz   k(  sJ y# 1 sw Y   <xY w# 1 sw Y   @xY w# 1 sw Y   DxY w)	u=   타임아웃 시 총 호출 횟수가 MAX_RETRIES + 1 이다.r   Nr   r1   r   r   r   rV   )r   r   r   r   r   r   rp   rq   rr   r   r   
call_count)r&   r   r   r   s       r   ,test_timeout_retries_correct_number_of_timesz8TestCallApi.test_timeout_retries_correct_number_of_times  s    //<=AUAUXYAYZ?= 	8|$ 8]]:. 8%%7788	8
 ##x';';a'????8 88 8	8 	8s<   CC( CCCCCC	CC$c                (   ddl }|j                  j                  d      t        d      g}t	        d|      5  t	        d      5  t        j                  di | j                  }ddd       ddd       dk(  sJ y# 1 sw Y   xY w# 1 sw Y   xY w)	uA   첫 번째 타임아웃 후 두 번째 시도에서 성공한다.r   Nr   u   성공r   r   r   rV   )r   r   r   r   r   r   r   r   )r&   r   r   r)   s       r   -test_timeout_retry_succeeds_on_second_attemptz9TestCallApi.test_timeout_retry_succeeds_on_second_attempt  s    //<l8>TU?= 	=|$ =!**<T^^<=	= !!!= =	= 	=s#   B A<$B<B	BBc                   ddl }t               }d|_        d|_        |j                  j                  |      }|gt        j                  dz   z  }t        d|      5  t        d	      5  t        j                  t              5 }t        j                  d
i | j                   ddd       ddd       ddd       j                  j                  dk(  sJ y# 1 sw Y   5xY w# 1 sw Y   9xY w# 1 sw Y   =xY w)u4   HTTP 에러 발생 시 재시도 후 exit(1) 한다.r   Ni  zInternal Server Error)responser1   r   r   r   rV   )r   r   status_codetextr   	HTTPErrorr   r   r   rp   rq   rr   r   r   r    rs   )r&   r   r   http_excr   ru   s         r   test_http_error_triggers_retryz*TestCallApi.test_http_error_triggers_retry+  s    K	 #	0	??,,i,@ zX%9%9A%=>?= 	8|$ 8]]:. 8(%%7788	8
 ~~""a'''8 88 8	8 	8s<   C/)C# C#C#+C/C C##C,	(C//C8c                   t               }d|j                  _        dg i|j                  _        |gt        j
                  dz   z  }t        d|      5  t        d      5  t        j                  t              5 }t	        j                  di | j                   ddd       ddd       ddd       j                  j                  dk(  sJ y# 1 sw Y   5xY w# 1 sw Y   9xY w# 1 sw Y   =xY w)uX   choices 가 없는 응답은 파싱 오류로 처리하고 재시도 후 exit(1) 한다.Nr   r1   r   r   r   rV   )r   r   r   r   r   r   r   rp   rq   rr   r   r   r    rs   r&   r   r   ru   s       r   test_response_parsing_errorz'TestCallApi.test_response_parsing_error=  s    K	26	""/'0"o	#!{h&:&:Q&>??= 	8|$ 8]]:. 8(%%7788	8
 ~~""a'''8 88 8	8 	8s<   C%C9 CC!C%CCC"	C%%C.c                    t               }d|j                  _        dddddigi|j                  _        t	        d|      5  t        j                  d	i | j                  }ddd       dk(  sJ y# 1 sw Y   xY w)
u=   응답 content 가 None 이면 빈 문자열을 반환한다.Nr   r   r   r   r   r   rL   rV   )r   r   r   r   r   r   r   r   )r&   r   r)   s      r   &test_content_none_returns_empty_stringz2TestCallApi.test_content_none_returns_empty_stringL  sz    K	26	""/'0I`d?e3f2g&h	#?; 	9&&88F	9 ||	9 	9s    A22A;c                X   ddl }|j                  j                  d      t        d      g}t	        d|      5  t	        d      5 }t        j                  di | j                   ddd       ddd       j                  t
        j                         y# 1 sw Y   1xY w# 1 sw Y   5xY w)	u=   재시도 시 time.sleep 이 RETRY_DELAY 초로 호출된다.r   Nr   rA   r   r   r   rV   )
r   r   r   r   r   r   r   r   assert_called_once_withRETRY_DELAY)r&   r   r   
mock_sleeps       r   test_sleep_called_on_retryz&TestCallApi.test_sleep_called_on_retryW  s     OO##K0

 ?= 	4|$ 4
!!3DNN34	4 	**8+?+?@4 4	4 	4s#   B  B$B B	B  B)c                    t        dt                     5  t        d      5 }t        j                  di | j                   ddd       ddd       j                          y# 1 sw Y   "xY w# 1 sw Y   &xY w)uS   첫 번째 시도(재시도 없음)에서는 time.sleep 을 호출하지 않는다.r   r   r   NrV   )r   r   r   r   r   assert_not_called)r&   r   s     r   test_no_sleep_on_first_attemptz*TestCallApi.test_no_sleep_on_first_attemptf  se    ?@ 	4|$ 4
!!3DNN34	4 	$$&4 4	4 	4s"   A/ A#A/#A,	(A//A8c                   t               }d|j                  _        t        j                  ddd      |j                  _        |gt        j                  dz   z  }t        d|      5  t        d      5  t        j                  t              5 }t        j                  d	i | j                   ddd       ddd       ddd       j                  j                  dk(  sJ y# 1 sw Y   5xY w# 1 sw Y   9xY w# 1 sw Y   =xY w)
u7   JSON 디코딩 오류 시 재시도 후 exit(1) 한다.NerrrL   r   r1   r   r   r   rV   )r   r   r   r   JSONDecodeErrorr   r   r   r   rp   rq   rr   r   r   r    rs   r   s       r   test_json_decode_error_retriesz*TestCallApi.test_json_decode_error_retriesn  s    K	26	""/%)%9%9%Q%G	"!{h&:&:Q&>??= 	8|$ 8]]:. 8(%%7788	8
 ~~""a'''8 88 8	8 	8s<   &C82C, C ,C,4C8 C)%C,,C5	1C88DNrQ   )rR   rS   rT   rU   r]   r   r   r   r   r   r   r   r   r   r   r   r   rV   r   r   rz   rz      sX    :I'A(@
"($(	A'(r   rz   c                      e Zd ZdZddZddZddZddZddZddZ	ddZ
dd	Zdd
ZddZddZddZddZddZy)TestBuildParseru   build_parser 단위 테스트c                    t         j                         }t        j                  t              5  |j                  g d       ddd       y# 1 sw Y   yxY w)u:   --task 와 --task-file 은 동시에 사용할 수 없다.)--taskr~   --task-filezsome.txtNr   build_parserrp   rq   rr   
parse_argsr&   parsers     r   .test_task_and_task_file_are_mutually_exclusivez>TestBuildParser.test_task_and_task_file_are_mutually_exclusive  sD    &&(]]:& 	NLM	N 	N 	N   AAc                    t         j                         }t        j                  t              5  |j                  g        ddd       y# 1 sw Y   yxY w)u<   --task 와 --task-file 중 하나는 반드시 필요하다.Nr   r   s     r   &test_one_of_task_or_task_file_requiredz6TestBuildParser.test_one_of_task_or_task_file_required  s?    &&(]]:& 	"b!	" 	" 	"s   A		Ac                t    t         j                         }|j                  ddg      }|j                  dk(  sJ y)u#   --role 기본값은 general 이다.r   r~   generalN)r   r   r   r   r&   r   argss      r   test_default_role_is_generalz,TestBuildParser.test_default_role_is_general  s7    &&(  (G!45yyI%%%r   c                t    t         j                         }|j                  ddg      }|j                  dk(  sJ y)u"   --model 기본값은 glm-5 이다.r   r~   r|   N)r   r   r   r   r   s      r   test_default_model_is_glm5z*TestBuildParser.test_default_model_is_glm5  s7    &&(  (G!45zzW$$$r   c                t    t         j                         }|j                  ddg      }|j                  dk(  sJ y)u&   --max-tokens 기본값은 8192 이다.r   r~   i    N)r   r   r   r   r   s      r   test_default_max_tokens_is_8192z/TestBuildParser.test_default_max_tokens_is_8192  s7    &&(  (G!45$&&&r   c                    t         j                         }t        t         j                  j	                               }|D ](  }|j                  ddd|g      }|j                  |k(  r(J  y)u<   유효한 role 목록이 SYSTEM_PROMPTS 키와 일치한다.r   r~   --roleN)r   r   setSYSTEM_PROMPTSkeysr   r   )r&   r   expected_rolesr   r   s        r   test_valid_rolesz TestBuildParser.test_valid_roles  sc    &&(X4499;<" 	%D$$h4%HID99$$$	%r   c                    t         j                         }t        j                  t              5  |j                  g d       ddd       y# 1 sw Y   yxY w)u6   정의되지 않은 role 을 지정하면 exit 한다.)r   r~   r   nonexistent_roleNr   r   s     r   test_invalid_role_exitsz'TestBuildParser.test_invalid_role_exits  sD    &&(]]:& 	QOP	Q 	Q 	Qr   c                    t         j                         }t         j                  D ](  }|j                  ddd|g      }|j                  |k(  r(J  y)u8   유효한 model 목록이 VALID_MODELS 와 일치한다.r   r~   --modelN)r   r   VALID_MODELSr   r   )r&   r   r   r   s       r   test_valid_modelsz!TestBuildParser.test_valid_models  sP    &&(** 	'E$$hE%JKD::&&&	'r   c                    t         j                         }t        j                  t              5  |j                  g d       ddd       y# 1 sw Y   yxY w)u7   정의되지 않은 model 을 지정하면 exit 한다.)r   r~   r   zgpt-999Nr   r   s     r   test_invalid_model_exitsz(TestBuildParser.test_invalid_model_exits  sD    &&(]]:& 	IGH	I 	I 	Ir   c                    t         j                         }|j                  ddg      }|j                  dk(  sJ |j                  J y)u0   --task-file 은 args.task_file 에 저장된다.r   zmy_task.txtN)r   r   r   	task_filetaskr   s      r   test_task_file_destz#TestBuildParser.test_task_file_dest  sG    &&(  -!?@~~...yy   r   c                t    t         j                         }|j                  g d      }|j                  dk(  sJ y)u-   --output 인수가 올바르게 파싱된다.)r   r~   --output/tmp/out.mdr   Nr   r   r   outputr   s      r   test_output_argumentz$TestBuildParser.test_output_argument  s3    &&(  !OP{{m+++r   c                n    t         j                         }|j                  ddg      }|j                  J y)u0   --output 을 지정하지 않으면 None 이다.r   r~   Nr   r   s      r   test_output_default_is_nonez+TestBuildParser.test_output_default_is_none  s5    &&(  (G!45{{"""r   c                d    h d}|t        t        j                  j                               k(  sJ y)uM   SYSTEM_PROMPTS 는 backend, frontend, uxui, tester, general 을 포함한다.>   uxuitesterbackendr   frontendN)r   r   r   r   )r&   expecteds     r   %test_system_prompts_has_expected_keysz5TestBuildParser.test_system_prompts_has_expected_keys  s)    G3x66;;=>>>>r   c                    dt         j                  v sJ dt         j                  v sJ dt         j                  v sJ dt         j                  v sJ y)u6   VALID_MODELS 상수가 4개의 모델을 포함한다.r|   glm-4.7zglm-4.7-flashzglm-4.7-flashxN)r   r   )r&   s    r   test_valid_models_constantz*TestBuildParser.test_valid_models_constant  sR    (/////H11111("7"77778#8#8888r   NrQ   )rR   rS   rT   rU   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rV   r   r   r   r     sN    'N"&%'%Q'I!,#?
9r   r   c                  p    e Zd ZdZddZddZddZddZddZddZ	ddZ
dd	Zdd
ZddZddZddZy)TestMainu+   main() CLI 통합 테스트 (모킹 기반)c                    t        j                  t        ddg|z         5  t        j	                          ddd       y# 1 sw Y   yxY w)u2   sys.argv 를 패치하여 main() 을 실행한다.argvzglm-callN)r   r^   sysr   main)r&   r   s     r   	_run_mainzTestMain._run_main  s7    \\#v
|d':; 	MMO	 	 	s	   >Ac                   t        j                  dddi      5  t        dt        d            5  | j                  ddg       d	d	d	       d	d	d	       |j	                         }d|j
                  v sJ y	# 1 sw Y   2xY w# 1 sw Y   6xY w)
u6   --task 인라인 입력이 stdout 으로 출력된다.rZ   rE   r{   r   u   inline 결과r   r   u   테스트 태스크N)r   r]   r   r   
readouterroutr&   capsyscaptureds      r   test_inline_task_stdout_outputz'TestMain.test_inline_task_stdout_output  s    ZZ}j&AB 	B\/5RS B*?@AB	B $$&(,,...	B B	B 	Bs"   BA5B5A>	:BB
c                \   |dz  }|j                  dd       t        j                  dddi      5  t        dt        d	      
      5  | j	                  dt        |      g       ddd       ddd       |j                         }d	|j                  v sJ y# 1 sw Y   2xY w# 1 sw Y   6xY w)u8   --task-file 파일 입력이 stdout 으로 출력된다.ztask.txtu   파일에서 읽은 태스크r   r   rZ   rE   r{   r   u   file 결과r   r   N)r"   r   r]   r   r   r$   r  r  )r&   r'   r  r   r  s        r   "test_task_file_input_stdout_outputz+TestMain.test_task_file_input_stdout_output  s    z)	<wOZZ}j&AB 	@\-5PQ @s9~>?@	@ $$&,,,	@ @	@ 	@s#   B"B%B"B	B""B+c           
     N   |dz  }t        j                  dddi      5  t        dt        d            5  | j                  dd	d
t	        |      g       ddd       ddd       |j                         sJ |j                  d      }d|v sJ y# 1 sw Y   <xY w# 1 sw Y   @xY w)u5   --output 지정 시 결과가 파일에 저장된다.z	result.mdrZ   rE   r{   r   u   저장될 내용r   r   	   태스크r   Nr   r   )r   r]   r   r   r$   exists	read_textr&   r'   output_filer   s       r   test_output_file_savedzTestMain.test_output_file_saved  s    ,ZZ}j&AB 	V\BT5UV V+z3{CSTUV	V !!###'''9!W,,,V V	V 	Vs"   BBBB	BB$c                2   t        |dz        }t        j                  dddi      5  t        j                  t
              5 }| j                  d|g       ddd       ddd       j                  j                  dk(  sJ y# 1 sw Y   -xY w# 1 sw Y   1xY w)u@   존재하지 않는 --task-file 을 지정하면 exit(1) 한다.zmissing.txtrZ   rE   r{   r   Nr1   )	r$   r   r]   rp   rq   rr   r   r    rs   )r&   r'   missing_fileru   s       r    test_nonexistent_task_file_exitsz)TestMain.test_nonexistent_task_file_exits  s    8m34ZZ}j&AB 	>z* >h|<=>	> ~~""a'''> >	> 	>s#   BBBB
	BBc                   t        j                  dddi      5  t        j                  t              5 }| j                  ddg       ddd       ddd       j                  j                  dk(  sJ y# 1 sw Y   -xY w# 1 sw Y   1xY w)u+   빈 --task 를 지정하면 exit(1) 한다.rZ   rE   r{   r   z   Nr1   )r   r]   rp   rq   rr   r   r    rs   )r&   ru   s     r   test_empty_task_exitszTestMain.test_empty_task_exits#  sx    ZZ}j&AB 	2z* 2h%012	2 ~~""a'''2 2	2 	2s"   A?A3A?3A<	8A??Bc                X   |dz  }|j                  dd       t        j                  dddi      5  t        j                  t
              5 }| j                  dt        |      g       d	d	d	       d	d	d	       j                  j                  d
k(  sJ y	# 1 sw Y   -xY w# 1 sw Y   1xY w)u7   내용이 비어 있는 --task-file 은 exit(1) 한다.z	empty.txtz   
r   r   rZ   rE   r{   r   Nr1   )
r"   r   r]   rp   rq   rr   r   r$   r    rs   )r&   r'   
empty_fileru   s       r   test_empty_task_file_exitsz#TestMain.test_empty_task_file_exits+  s    +
g8ZZ}j&AB 	Az* Ahs:?@A	A ~~""a'''A A	A 	As#   B B(B B	B  B)c                   t        j                  dddi      5  t        j                  t        dd      5 }| j	                  g d       ddd       ddd       j                          |j                  }|j                  j                  d	      xs |d
   j                  d	      }|t        j                  d   k(  sJ y# 1 sw Y   wxY w# 1 sw Y   {xY w)u=   --role 인수가 call_api 의 system_prompt 에 반영된다.rZ   rE   r{   r   mock resultr   )r   r  r   r   Nr   r1   r   )
r   r]   r^   r   r   r   r   r   rB   r   )r&   	mock_callr   r   s       r   test_role_passed_to_call_apiz%TestMain.test_role_passed_to_call_api6  s    ZZ}j&AB 	Mh
O MS\KLM	M 	$$&))#**..?f;q>CUCUVeCf 7 7	 BBBBM M	M 	Ms"   CC 
C C		CCc                n   t        j                  dddi      5  t        j                  t        dd      5 }| j	                  g d       ddd       ddd       j
                  }|j                  j                  d	      xs |d
   j                  d	      }|dk(  sJ y# 1 sw Y   VxY w# 1 sw Y   ZxY w)u,   --model 인수가 call_api 에 전달된다.rZ   rE   r{   r   r  r   )r   r  r   r   Nr   r1   r   r   r]   r^   r   r   r   r   rB   )r&   r  r   r   s       r   test_model_passed_to_call_apiz&TestMain.test_model_passed_to_call_apiA  s    ZZ}j&AB 	Nh
O NS\LMN	N  ))""&&w/N;q>3E3Eg3N	!!!N N	N 	N"   B+B
B+B(	$B++B4c           
     D   |dz  }t        j                  dddi      5  t        dt        d            5  | j                  dd	d
t	        |      g       ddd       ddd       |j                  d      }|j                  d      sJ y# 1 sw Y   7xY w# 1 sw Y   ;xY w)uD   결과에 개행 문자가 없으면 파일 저장 시 추가된다.zout.txtrZ   rE   r{   r   
no newliner   r   r  r   Nr   r   
)r   r]   r   r   r$   r  endswithr  s       r   'test_output_newline_appended_if_missingz0TestMain.test_output_newline_appended_if_missingK  s    *ZZ}j&AB 	V\,5OP V+z3{CSTUV	V '''9%%%	V V	V 	Vs"   BB
B
B	BBc                4   t        j                  dddi      5  t        dt        d            5  | j                  ddg       d	d	d	       d	d	d	       |j	                         }|j
                  j                  d
      sJ y	# 1 sw Y   ?xY w# 1 sw Y   CxY w)uN   결과에 개행 문자가 없어도 stdout 출력 시 개행이 추가된다.rZ   rE   r{   r   r"  r   r   r  Nr#  )r   r]   r   r   r  r  r$  r  s      r   #test_output_stdout_newline_appendedz,TestMain.test_output_stdout_newline_appendedV  s    ZZ}j&AB 	8\,5OP 8+678	8 $$&||$$T***	8 8	8 	8s"   BBBB	BBc                n   t        j                  dddi      5  t        j                  t        dd      5 }| j	                  g d       ddd       ddd       j
                  }|j                  j                  d	      xs |d
   j                  d	      }|dk(  sJ y# 1 sw Y   VxY w# 1 sw Y   ZxY w)u1   --max-tokens 인수가 call_api 에 전달된다.rZ   rE   r{   r   r  r   )r   r  z--max-tokens512Nr   r1   i   r  )r&   r  r   r   s       r   "test_max_tokens_passed_to_call_apiz+TestMain.test_max_tokens_passed_to_call_api_  s    ZZ}j&AB 	Oh
O OS\MNO	O  )) ''++L9][^=O=OP\=]
S   O O	O 	Or   N)r   z	list[str]rO   rP   )r  pytest.CaptureFixturerO   rP   )r'   r   r  r+  rO   rP   rN   rQ   )rR   rS   rT   rU   r   r  r	  r  r  r  r  r  r  r%  r'  r*  rV   r   r   r   r     sB    5
/
-
-((	(	C"	&+!r   r   )u   응답 텍스트)r   r$   rO   r   )#rU   
__future__r   	importlibr   r   typespathlibr   unittest.mockr   r   rp   importlib.utilutil_iluspec_from_file_location_specmodule_from_specr   loaderexec_module_IMPORT_ERROR	Exceptionexc
ModuleTypemarkskipif
pytestmarkr   r   rX   rz   r   r   rV   r   r   <module>r@     s    #   
   * 
!(D((5\]E~~..u5H	LLX&
 M[[+M?;   
^ ^L6% 6%|R( R(te9 e9Zw! w!S  u0HMs   AC C:C55C: