
    ^9i`                       d Z ddlmZ ddlZddlmc 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(                  j1                  e      Zej4                  j7                  e       dZej@                  jC                  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j>                  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     0/home/jay/workspace/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(  }|st	        j
                  d|fd||f      d	t        j                         v st	        j                  |      rt	        j                  |      nd	t	        j                  |      d
z  }dd|iz  }t        t	        j                  |            dx}}y)u0   KEY="value" 형식을 올바르게 파싱한다.	.env.keyszKEY="value"
utf-8encodingKEYvalue==z%(py0)s == %(py3)sresultpy0py3assert %(py5)spy5N
write_textr   _parse_env_keys_filestr
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationselftmp_pathenv_filer$   @py_assert2@py_assert1@py_format4@py_format6s           r   test_basic_key_valuez)TestParseEnvKeysFile.test_basic_key_valueH   s    k)Og>..s8}=))v)))))v)))))))v)))v)))))))))))r   c                   |dz  }|j                  dd       t        j                  t        |            }ddi}||k(  }|st	        j
                  d|fd||f      d	t        j                         v st	        j                  |      rt	        j                  |      nd	t	        j                  |      d
z  }dd|iz  }t        t	        j                  |            dx}}y)u?   export KEY=value 형식에서 export 접두어를 제거한다.r   zexport MY_KEY=my_value
r   r   MY_KEYmy_valuer!   r#   r$   r%   r(   r)   Nr*   r6   s           r   test_export_prefixz'TestParseEnvKeysFile.test_export_prefixQ   s    k)6I..s8}="J//v/////v///////v///v///////////r   c                   |dz  }|j                  dd       t        j                  t        |            }d}||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndd	z  }d
d|iz  }t        t	        j                  |            dx}}t        |      }d}||k(  }	|	st	        j
                  d|	fd||f      dt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }
dd|
iz  }t        t	        j                  |            dx}x}	}y)u9   # 로 시작하는 주석 줄과 빈 줄은 무시한다.r   u-   # 이것은 주석입니다

VALID_KEY=hello
r   r   	VALID_KEYinz%(py1)s in %(py3)sr$   py1r'   r(   r)   N   r!   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slen)r&   rI   r'   py6zassert %(py8)spy8)r+   r   r,   r-   r.   r/   r3   r0   r1   r2   r4   r5   rK   )r7   r8   r9   r$   @py_assert0r:   r<   r=   @py_assert5@py_assert4@py_format7@py_format9s               r   test_comment_lines_ignoredz/TestParseEnvKeysFile.test_comment_lines_ignoredZ   s   k)NY`a..s8}=${f$$$${f$$${$$$$$$f$$$f$$$$$$$6{a{a{ass66{ar   c                p   |dz  }|j                  dd       t        j                  t        |            }|d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      d	z  }d
d|iz  }t        t	        j                  |            dx}x}}y)u8   큰따옴표로 감싼 값의 따옴표를 제거한다.r   zTOKEN="abc123"
r   r   TOKENabc123r!   z%(py1)s == %(py4)srI   py4assert %(py6)srL   N	r+   r   r,   r-   r.   r/   r3   r4   r5   	r7   r8   r9   r$   rN   @py_assert3r:   @py_format5rQ   s	            r   test_double_quote_removalz.TestParseEnvKeysFile.test_double_quote_removald       k).A..s8}=g*(*(****(******(*******r   c                p   |dz  }|j                  dd       t        j                  t        |            }|d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      d	z  }d
d|iz  }t        t	        j                  |            dx}x}}y)u;   작은따옴표로 감싼 값의 따옴표를 제거한다.r   zTOKEN='abc123'
r   r   rU   rV   r!   rW   rX   rZ   rL   Nr[   r\   s	            r   test_single_quote_removalz.TestParseEnvKeysFile.test_single_quote_removalm   r`   r   c                z   t         j                  d      }i }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y)	u;   파일이 존재하지 않으면 빈 dict 를 반환한다.z/nonexistent/path/.env.keysr!   r#   r$   r%   r(   r)   N)
r   r,   r.   r/   r0   r1   r2   r3   r4   r5   r7   r$   r:   r;   r<   r=   s         r   &test_file_not_found_returns_empty_dictz;TestParseEnvKeysFile.test_file_not_found_returns_empty_dictv   sk    ../LMv|vvvr   c                   |dz  }|j                  dd       t        j                  t        |            }d}||v}|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndd	z  }d
d|iz  }t        t	        j                  |            dx}}|j                  }d}	 ||	      }
d}|
|k(  }|st	        j
                  d|fd|
|f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |	      t	        j                  |
      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}	x}
x}}y)u!   = 가 없는 줄은 무시한다.r   zINVALID_LINE
VALID=ok
r   r   INVALID_LINE)not in)z%(py1)s not in %(py3)sr$   rH   r(   r)   NVALIDokr!   zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} == %(py9)sr&   py2rY   rL   py9assert %(py11)spy11)r+   r   r,   r-   r.   r/   r3   r0   r1   r2   r4   r5   get)r7   r8   r9   r$   rN   r:   r<   r=   r;   r]   rO   @py_assert8@py_assert7@py_format10@py_format12s                  r    test_line_without_equals_ignoredz5TestParseEnvKeysFile.test_line_without_equals_ignored|   s%   k)6I..s8}=+~V++++~V+++~++++++V+++V+++++++zz*'*z'"*d*"d****"d******v***v***z***'***"***d********r   c                p   |dz  }|j                  dd       t        j                  t        |            }|d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      d	z  }d
d|iz  }t        t	        j                  |            dx}x}}|d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      d	z  }d
d|iz  }t        t	        j                  |            dx}x}}y)u%   여러 키를 동시에 파싱한다.r   z1GLM_API_KEY="key-xxx"
export OTHER_KEY=other_val
r   r   GLM_API_KEYzkey-xxxr!   rW   rX   rZ   rL   N	OTHER_KEY	other_valr[   r\   s	            r   test_multiple_keysz'TestParseEnvKeysFile.test_multiple_keys   s    k)A 	 	

 ..s8}=m$1	1$	1111$	111$111	1111111k"1k1"k1111"k111"111k1111111r   c                p   |dz  }|j                  dd       t        j                  t        |            }|d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      d	z  }d
d|iz  }t        t	        j                  |            dx}x}}y)uM   값 안에 = 가 포함된 경우 첫 번째 = 를 기준으로 분리한다.r   zTOKEN=abc=def=ghi
r   r   rU   zabc=def=ghir!   rW   rX   rZ   rL   Nr[   r\   s	            r   test_value_with_equals_signz0TestParseEnvKeysFile.test_value_with_equals_sign   s    k)1GD..s8}=g/-/-////-//////-///////r   c                   |dz  }|j                  dd       t        j                  t        |            }i }||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }d	d
|iz  }t        t	        j                  |            dx}}y)u(   빈 파일은 빈 dict 를 반환한다.r    r   r   r!   r#   r$   r%   r(   r)   Nr*   r6   s           r   test_empty_filez$TestParseEnvKeysFile.test_empty_file   s    k)B1..s8}=v|vvvr   Nr8   r   returnNoner   r   )__name__
__module____qualname____doc__r>   rB   rS   r_   rb   re   rv   r{   r}   r    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                t   |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(  }|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}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.environrx   zenv-keyENV_KEYS_PATHNr!   r#   r$   r%   r(   r)   r+   r   dictobjectr   r-   load_api_keyr.   r/   r0   r1   r2   r3   r4   r5   r6   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 #"v""""v""""""v"""v""""""""""1 1	1 	1#   %D.D"+D."D+	'D..D7c                   t        j                  dddi      5  t        j                         }ddd       d}|k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }d	d
|iz  }t        t	        j                  |            dx}}y# 1 sw Y   xY w)u;   환경변수만 존재할 때 정상적으로 반환한다.r   rx   z
my-api-keyNr!   r#   r$   r%   r(   r)   )r   r   r   r   r.   r/   r0   r1   r2   r3   r4   r5   rd   s         r   test_env_var_onlyz TestLoadApiKey.test_env_var_only   s    ZZ}l&CD 	-**,F	- &%v%%%%v%%%%%%v%%%v%%%%%%%%%%	- 	-s   CC&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(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }	dd|	iz  }
t!        t        j"                  |
            dx}}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   osrx   r   Tclearr   Nzfile-api-keyr!   r#   r$   r%   r(   r)   )r+   
__import__environitemsr   r   r   r   r-   r   r.   r/   r0   r1   r2   r3   r4   r5   )r7   r8   r9   kvenv_without_keyr$   r:   r;   r<   r=   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 ('v''''v''''''v'''v'''''''''' d1 1	1 	1s/   EE-%E0E$'E0$E-	)E00E9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(  }
|
st        j                  d
|
fd||	f      dt!        j"                         v st        j$                  |      rt        j&                  |      ndt        j&                  |      t        j&                  |      t        j&                  |	      dz  }dd|iz  }t)        t        j*                  |            dx}x}x}
}	yc c}}w # 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w)uH   환경변수도 없고 파일도 없으면 sys.exit(1) 을 호출한다.znonexistent.keysr   rx   r   Tr   r   NrJ   r!   zG%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.value
}.code
} == %(py7)sexc_infor&   rm   rY   py7assert %(py9)srn   )r-   r   r   r   r   r   r   r   pytestraises
SystemExitr   r    coder.   r/   r0   r1   r2   r3   r4   r5   )r7   r8   missing_pathr   r   r   r   r;   r]   @py_assert6rO   @py_format8rt   s                r   test_no_key_anywhere_exitsz)TestLoadApiKey.test_no_key_anywhere_exits   sQ   8&889,6t,<,D,D,J,J,LcDAqPQUbPb1a4ccZZoTB 	,hF ,]]:. ,())+,,	,
 ~~'~""'a'"a''''"a''''''x'''x'''~'''"'''a''''''' d, ,, ,	, 	,sG   F1F1#G?GF7.G6G7G<GG		GGc                t   |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(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}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   r   rx   r   r   Nz	from-filer!   r#   r$   r%   r(   r)   r   r6   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 %$v$$$$v$$$$$$v$$$v$$$$$$$$$$1 1	1 	1r   Nr   r   )	r   r   r   r   r   r   r   r   r   r   r   r   r   r      s    '	#&
(
(	%r   r   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(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}}j                          y# 1 sw Y   xY w)u7   정상 응답에서 assistant content 를 반환한다.requests.postu   정상 응답r   Nr!   r#   r$   r%   r(   r)   r   )r   r   r   call_api	_DEFAULTSr.   r/   r0   r1   r2   r3   r4   r5   assert_called_once)r7   	mock_postr$   r:   r;   r<   r=   s          r   test_successful_responsez$TestCallApi.test_successful_response   s    ?o1NO 	9S\&&88F	9 )(v((((v((((((v(((v(((((((((($$&		9 	9s    C66C?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(  }|st        j                  d|fd	||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      d
z  }	dd|	iz  }
t        t        j                  |
            dx}x}x}x}}y# 1 sw Y   GxY w)u6   Authorization 헤더에 Bearer 토큰이 포함된다.r   r   NheadersrJ   AuthorizationzBearer test-keyr!   rk   rl   ro   rp   r   )r   r   r   r   r   	call_argskwargsrq   r.   r/   r0   r1   r2   r3   r4   r5   )r7   r   call_kwargsr   r;   r]   rO   rr   rs   rt   ru   s              r   *test_request_includes_authorization_headerz6TestCallApi.test_request_includes_authorization_header   s   ?@ 	0I//	0  ))$$((3X{1~7I7I)UW7X{{@?@{?+@/@@+/@@@@@+/@@@@@@@w@@@w@@@{@@@?@@@+@@@/@@@@@@@@@	0 	0s    E;;Fc                d   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(  }|st        j                  d|fd	||f      d
t        j                          v st        j"                  |      rt        j$                  |      nd
t        j$                  |      t        j$                  |      t        j$                  |      dz  }dd|iz  }	t'        t        j(                  |	            dx}x}x}}y# 1 sw Y   xY w# 1 sw Y   
xY w# 1 sw Y   xY w)uH   타임아웃 발생 시 MAX_RETRIES 만큼 재시도 후 exit(1) 한다.r   N	timed outrJ   r   side_effect
time.sleepr!   r   r   r   r   rn   r   )requests
exceptionsTimeoutr   MAX_RETRIESr   r   r   r   r   r   r    r   r.   r/   r0   r1   r2   r3   r4   r5   )
r7   _reqside_effectsr   r;   r]   r   rO   r   rt   s
             r   test_timeout_triggers_retryz'TestCallApi.test_timeout_triggers_retry  s)   //<=AUAUXYAYZ?= 	8|$ 8]]:. 8(%%7788	8
 ~~'~""'a'"a''''"a''''''x'''x'''~'''"'''a'''''''8 88 8	8 	8s<   F%F( FFF%FFF"	F%%F/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
t        j                  d|fd	||f      d
t        j                         v st        j                   |      rt        j"                  |      nd
t        j"                  |      dt        j                         v st        j                   t              rt        j"                  t              ndt        j"                  |      t        j"                  |      dz  }	dd|	iz  }
t%        t        j&                  |
            dx}x}x}x}}y# 1 sw Y   ZxY w# 1 sw Y   _xY w# 1 sw Y   dxY w)u=   타임아웃 시 총 호출 횟수가 MAX_RETRIES + 1 이다.r   Nr   rJ   r   r   r   r!   )z_%(py2)s
{%(py2)s = %(py0)s.call_count
} == (%(py6)s
{%(py6)s = %(py4)s.MAX_RETRIES
} + %(py8)s)r   r   r&   rm   rY   rL   rM   ro   rp   r   )r   r   r   r   r   r   r   r   r   r   r   
call_countr.   r/   r0   r1   r2   r3   r4   r5   )r7   r   r   r   r;   rO   rs   @py_assert9r]   rt   ru   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?';a'??#'?????#'???????y???y???#??????x???x???';???a????????8 88 8	8 	8s<   G:G-( G G-G: G*%G--G7	2G::Hc                f   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(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w# 1 sw Y   xY w)uA   첫 번째 타임아웃 후 두 번째 시도에서 성공한다.r   Nr   u   성공r   r   r   r!   r#   r$   r%   r(   r)   r   )r   r   r   r   r   r   r   r   r.   r/   r0   r1   r2   r3   r4   r5   )r7   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^^<=	= "!v!!!!v!!!!!!v!!!v!!!!!!!!!!= =	= 	=s#   D' D$D'D$	 D''D0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(  }	|	st!        j"                  d
|	fd||f      dt%        j&                         v st!        j(                  |      rt!        j*                  |      ndt!        j*                  |      t!        j*                  |      t!        j*                  |      dz  }
dd|
iz  }t-        t!        j.                  |            dx}x}x}	}y# 1 sw Y   xY w# 1 sw Y   
xY w# 1 sw Y   xY w)u4   HTTP 에러 발생 시 재시도 후 exit(1) 한다.r   Ni  zInternal Server Error)responserJ   r   r   r   r!   r   r   r   r   rn   r   )r   r   status_codetextr   	HTTPErrorr   r   r   r   r   r   r   r   r    r   r.   r/   r0   r1   r2   r3   r4   r5   )r7   r   r   http_excr   r   r;   r]   r   rO   r   rt   s               r   test_http_error_triggers_retryz*TestCallApi.test_http_error_triggers_retry+  sE   K	 #	0	??,,i,@ zX%9%9A%=>?= 	8|$ 8]]:. 8(%%7788	8
 ~~'~""'a'"a''''"a''''''x'''x'''~'''"'''a'''''''8 88 8	8 	8s<   G )F3 F&#F3+G &F0+F33F=	8G  G
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(  }|st        j                  d|fd||f      d	t!        j"                         v st        j$                  |      rt        j&                  |      nd	t        j&                  |      t        j&                  |      t        j&                  |      d
z  }dd|iz  }	t)        t        j*                  |	            dx}x}x}}y# 1 sw Y   xY w# 1 sw Y   
xY w# 1 sw Y   xY w)uX   choices 가 없는 응답은 파싱 오류로 처리하고 재시도 후 exit(1) 한다.Nr   rJ   r   r   r   r!   r   r   r   r   rn   r   )r   r   r   r   r   r   r   r   r   r   r   r   r    r   r.   r/   r0   r1   r2   r3   r4   r5   
r7   r   r   r   r;   r]   r   rO   r   rt   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'"a''''"a''''''x'''x'''~'''"'''a'''''''8 88 8	8 	8s<   F6F)9 FF)!F6F&!F))F3	.F66G 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(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w)u=   응답 content 가 None 이면 빈 문자열을 반환한다.Nr   r   r   r   r   r   r   r!   r#   r$   r%   r(   r)   r   )r   r   r   r   r   r   r   r   r.   r/   r0   r1   r2   r3   r4   r5   )r7   r   r$   r:   r;   r<   r=   s          r   &test_content_none_returns_empty_stringz2TestCallApi.test_content_none_returns_empty_stringL  s    K	26	""/'0I`d?e3f2g&h	#?; 	9&&88F	9 v|vvv	9 	9s    DD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   rj   r   r   r   r   )
r   r   r   r   r   r   r   r   assert_called_once_withRETRY_DELAY)r7   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   Nr   )r   r   r   r   r   assert_not_called)r7   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(  }|st!        j"                  d	|fd
||f      dt%        j&                         v st!        j(                  |      rt!        j*                  |      ndt!        j*                  |      t!        j*                  |      t!        j*                  |      dz  }dd|iz  }	t-        t!        j.                  |	            dx}x}x}}y# 1 sw Y   xY w# 1 sw Y   
xY w# 1 sw Y   xY w)u7   JSON 디코딩 오류 시 재시도 후 exit(1) 한다.Nerrr   r   rJ   r   r   r   r!   r   r   r   r   rn   r   )r   r   r   r   JSONDecodeErrorr   r   r   r   r   r   r   r   r   r    r   r.   r/   r0   r1   r2   r3   r4   r5   r   s
             r   test_json_decode_error_retriesz*TestCallApi.test_json_decode_error_retriesn  sE   K	26	""/%)%9%9%Q%G	"!{h&:&:Q&>??= 	8|$ 8]]:. 8(%%7788	8
 ~~'~""'a'"a''''"a''''''x'''x'''~'''"'''a'''''''8 88 8	8 	8s<   &G	2F< F/,F<4G	/F94F<<G	G		GNr   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r      sX    :I'A(@
"($(	A'(r   r   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_parserr   r   r   
parse_argsr7   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         j                         }|j                  ddg      }|j                  }d}||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd	|iz  }t        t	        j                  |            d
x}x}}y
)u#   --role 기본값은 general 이다.r   r   generalr!   )z,%(py2)s
{%(py2)s = %(py0)s.role
} == %(py5)sargsr&   rm   r)   assert %(py7)sr   N)r   r   r   r   r.   r/   r0   r1   r2   r3   r4   r5   r7   r  r  r;   rP   r]   r=   r   s           r   test_default_role_is_generalz,TestBuildParser.test_default_role_is_general  s    &&(  (G!45yy%I%yI%%%%yI%%%%%%t%%%t%%%y%%%I%%%%%%%r   c                   t         j                         }|j                  ddg      }|j                  }d}||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd	|iz  }t        t	        j                  |            d
x}x}}y
)u"   --model 기본값은 glm-5 이다.r   r   r   r!   )z-%(py2)s
{%(py2)s = %(py0)s.model
} == %(py5)sr  r	  r
  r   N)r   r   r   r   r.   r/   r0   r1   r2   r3   r4   r5   r  s           r   test_default_model_is_glm5z*TestBuildParser.test_default_model_is_glm5  s    &&(  (G!45zz$W$zW$$$$zW$$$$$$t$$$t$$$z$$$W$$$$$$$r   c                   t         j                         }|j                  ddg      }|j                  }d}||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd	|iz  }t        t	        j                  |            d
x}x}}y
)u&   --max-tokens 기본값은 8192 이다.r   r   i    r!   )z2%(py2)s
{%(py2)s = %(py0)s.max_tokens
} == %(py5)sr  r	  r
  r   N)r   r   r   r   r.   r/   r0   r1   r2   r3   r4   r5   r  s           r   test_default_max_tokens_is_8192z/TestBuildParser.test_default_max_tokens_is_8192  s    &&(  (G!45&$&$&&&&$&&&&&&t&&&t&&&&&&$&&&&&&&r   c                   t         j                         }t        t         j                  j	                               }|D ]	  }|j                  ddd|g      }|j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }d	d
|iz  }t        t        j                  |            dx}} y)u<   유효한 role 목록이 SYSTEM_PROMPTS 키와 일치한다.r   r   --roler!   )z,%(py2)s
{%(py2)s = %(py0)s.role
} == %(py4)sr  r   r&   rm   rY   rZ   rL   N)r   r   setSYSTEM_PROMPTSkeysr   r   r.   r/   r0   r1   r2   r3   r4   r5   )	r7   r  expected_rolesr   r  r;   r]   r^   rQ   s	            r   test_valid_rolesz TestBuildParser.test_valid_roles  s    &&(X4499;<" 	%D$$h4%HID99$9$$$$9$$$$$$4$$$4$$$9$$$$$$$$$$$$$$$$	%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                f   t         j                         }t         j                  D ]	  }|j                  ddd|g      }|j                  }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }d	d
|iz  }t        t        j                  |            dx}} y)u8   유효한 model 목록이 VALID_MODELS 와 일치한다.r   r   --modelr!   )z-%(py2)s
{%(py2)s = %(py0)s.model
} == %(py4)sr  r   r  rZ   rL   N)r   r   VALID_MODELSr   r   r.   r/   r0   r1   r2   r3   r4   r5   )r7   r  r   r  r;   r]   r^   rQ   s           r   test_valid_modelsz!TestBuildParser.test_valid_models  s    &&(** 	'E$$hE%JKD::&:&&&&:&&&&&&4&&&4&&&:&&&&&&&&&&&&&&&&	'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                p   t         j                         }|j                  ddg      }|j                  }d}||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d	x}x}}|j                  }d	}||u }|st	        j
                  d
|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d	x}x}}y	)u0   --task-file 은 args.task_file 에 저장된다.r   zmy_task.txtr!   )z1%(py2)s
{%(py2)s = %(py0)s.task_file
} == %(py5)sr  r	  r
  r   Nis)z,%(py2)s
{%(py2)s = %(py0)s.task
} is %(py5)s)r   r   r   	task_filer.   r/   r0   r1   r2   r3   r4   r5   taskr  s           r   test_task_file_destz#TestBuildParser.test_task_file_dest  s    &&(  -!?@~~..~....~......t...t...~..........yy D yD    yD      t   t   y   D       r   c                   t         j                         }|j                  g d      }|j                  }d}||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            d	x}x}}y	)
u-   --output 인수가 올바르게 파싱된다.)r   r   --output/tmp/out.mdr*  r!   )z.%(py2)s
{%(py2)s = %(py0)s.output
} == %(py5)sr  r	  r
  r   Nr   r   r   outputr.   r/   r0   r1   r2   r3   r4   r5   r  s           r   test_output_argumentz$TestBuildParser.test_output_argument  s    &&(  !OP{{+m+{m++++{m++++++t+++t+++{+++m+++++++r   c                   t         j                         }|j                  ddg      }|j                  }d}||u }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd	|iz  }t        t	        j                  |            dx}x}}y)
u0   --output 을 지정하지 않으면 None 이다.r   r   Nr#  )z.%(py2)s
{%(py2)s = %(py0)s.output
} is %(py5)sr  r	  r
  r   r+  r  s           r   test_output_default_is_nonez+TestBuildParser.test_output_default_is_none  s    &&(  (G!45{{"d"{d""""{d""""""t"""t"""{"""d"""""""r   c           
     Z   h d}t         j                  }|j                  } |       }t        |      }||k(  }|sgt	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      nddt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  t               rt	        j                  t               ndt	        j                  |      t	        j                  |      t	        j                  |      t	        j                  |      dz  }dd	|iz  }t        t	        j                  |            d
x}x}x}x}}y
)uM   SYSTEM_PROMPTS 는 backend, frontend, uxui, tester, general 을 포함한다.>   uxuitesterbackendr  frontendr!   )z%(py0)s == %(py11)s
{%(py11)s = %(py2)s(%(py9)s
{%(py9)s = %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = %(py3)s.SYSTEM_PROMPTS
}.keys
}()
})
}expectedr  r   )r&   rm   r'   r)   r   rn   rp   zassert %(py13)spy13N)r   r  r  r  r.   r/   r0   r1   r2   r3   r4   r5   )	r7   r5  rP   r   rr   @py_assert10r;   ru   @py_format14s	            r   %test_system_prompts_has_expected_keysz5TestBuildParser.test_system_prompts_has_expected_keys  s    G'66>6;;>;=>3=>>x>>>>>x>>>>>>>x>>>x>>>>>>3>>>3>>>>>>x>>>x>>>6>>>;>>>=>>>>>>>>>>>>r   c                   d}t         j                  }||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}d	}t         j                  }||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}d
}t         j                  }||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}d}t         j                  }||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)u6   VALID_MODELS 상수가 4개의 모델을 포함한다.r   rE   )z4%(py1)s in %(py5)s
{%(py5)s = %(py3)s.VALID_MODELS
}r   rI   r'   r)   r
  r   Nglm-4.7zglm-4.7-flashzglm-4.7-flashx)
r   r  r.   r/   r3   r0   r1   r2   r4   r5   )r7   rN   rP   r:   r=   r   s         r   test_valid_models_constantz*TestBuildParser.test_valid_models_constant  s   /(///w/////w////w//////(///(///////////1H111y11111y1111y111111H111H111111111117("7"77"77777"7777777777(777(777"7777777788#8#88#88888#888888888888888888#88888888r   Nr   )r   r   r   r   r  r  r  r  r  r  r  r  r!  r'  r-  r/  r9  r=  r   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)r7   rA  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 }|st        j                  d
|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}y	# 1 sw Y   xY w# 1 sw Y   xY w)u6   --task 인라인 입력이 stdout 으로 출력된다.r   rx   r   r   u   inline 결과r   r   u   테스트 태스크NrE   z+%(py1)s in %(py5)s
{%(py5)s = %(py3)s.out
}capturedr;  r
  r   )r   r   r   rD  
readouterroutr.   r/   r3   r0   r1   r2   r4   r5   )r7   capsysrG  rN   rP   r:   r=   r   s           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"   D8D,D8,D5	1D88E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 }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y# 1 sw Y   xY w# 1 sw Y   xY w)u8   --task-file 파일 입력이 stdout 으로 출력된다.ztask.txtu   파일에서 읽은 태스크r   r   r   rx   r   r   u   file 결과r   r   NrE   rF  rG  r;  r
  r   )r+   r   r   r   rD  r-   rH  rI  r.   r/   r3   r0   r1   r2   r4   r5   )
r7   r8   rJ  r%  rG  rN   rP   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#   EE%EE	EE"c           
        |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
                  } |       }|sddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}|j                  d      }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }	dd|	iz  }
t        t        j                  |
            dx}}y# 1 sw Y   pxY w# 1 sw Y   uxY w)u5   --output 지정 시 결과가 파일에 저장된다.z	result.mdr   rx   r   r   u   저장될 내용r   r   	   태스크r)  NzAassert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}output_filer  r   r   rE   rG   r   rH   r(   r)   )r   r   r   rD  r-   existsr0   r1   r.   r2   r3   r4   r5   	read_textr/   )r7   r8   rP  r;   r]   r^   r   rN   r:   r<   r=   s              r   test_output_file_savedzTestMain.test_output_file_saved  sJ   ,ZZ}j&AB 	V\BT5UV V+z3{CSTUV	V !!#!########{###{###!##########'''9!,!W,,,,!W,,,!,,,,,,W,,,W,,,,,,,V V	V 	Vs"   GGGG	GGc                   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(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                   |	            dx}x}x}}y# 1 sw Y   xY w# 1 sw Y   xY w)u@   존재하지 않는 --task-file 을 지정하면 exit(1) 한다.zmissing.txtr   rx   r   r   NrJ   r!   r   r   r   r   rn   )r-   r   r   r   r   r   rD  r    r   r.   r/   r0   r1   r2   r3   r4   r5   )
r7   r8   missing_filer   r;   r]   r   rO   r   rt   s
             r    test_nonexistent_task_file_exitsz)TestMain.test_nonexistent_task_file_exits  s    8m34ZZ}j&AB 	>z* >h|<=>	> ~~'~""'a'"a''''"a''''''x'''x'''~'''"'''a'''''''> >	> 	>s#   EEEE	EE&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(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}}y# 1 sw Y   xY w# 1 sw Y   xY w)u+   빈 --task 를 지정하면 exit(1) 한다.r   rx   r   r   z   NrJ   r!   r   r   r   r   rn   )r   r   r   r   r   rD  r    r   r.   r/   r0   r1   r2   r3   r4   r5   )r7   r   r;   r]   r   rO   r   rt   s           r   test_empty_task_exitszTestMain.test_empty_task_exits#  s    ZZ}j&AB 	2z* 2h%012	2 ~~'~""'a'"a''''"a''''''x'''x'''~'''"'''a'''''''2 2	2 	2s"   EEEE	EEc                   |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(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }	t!        t        j"                  |	            d	x}x}x}}y	# 1 sw Y   xY w# 1 sw Y   xY w)u7   내용이 비어 있는 --task-file 은 exit(1) 한다.z	empty.txtz   
r   r   r   rx   r   r   NrJ   r!   r   r   r   r   rn   )r+   r   r   r   r   r   rD  r-   r    r   r.   r/   r0   r1   r2   r3   r4   r5   )
r7   r8   
empty_filer   r;   r]   r   rO   r   rt   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''''''x'''x'''~'''"'''a'''''''A A	A 	As#   E/E#(E/#E,	(E//E9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(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      dz  }dd|iz  }t!        t        j"                  |            dx}}y# 1 sw Y   xY w# 1 sw Y   xY w)u=   --role 인수가 call_api 의 system_prompt 에 반영된다.r   rx   r   r   mock resultr   )r   rO  r  r3  Nr   rJ   r3  r!   r#   r%   r(   r)   )r   r   r   r   rD  r   r   r   rq   r  r.   r/   r0   r1   r2   r3   r4   r5   )r7   	mock_callr   r   r:   r;   r<   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BBB} BBBBBBB}BBB}BBB BBBBBBBBM M	M 	Ms"   E,E
E,E)	$E,,E6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                  d	      xs |d
   j                  d	      }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w# 1 sw Y   xY w)u,   --model 인수가 call_api 에 전달된다.r   rx   r   r   r]  r   )r   rO  r  r<  Nr   rJ   r<  r!   r#   r%   r(   r)   r   r   r   r   rD  r   r   rq   r.   r/   r0   r1   r2   r3   r4   r5   )r7   r^  r   r   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!!u	!!!!u	!!!!!!u!!!u!!!	!!!!!!!N N	N 	N"   E
D>
E
>E	E

Ec           
        |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} ||      }|sddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      dz  }t        t        j                  |            dx}x}}y# 1 sw Y   xY w# 1 sw Y   xY w)uD   결과에 개행 문자가 없으면 파일 저장 시 추가된다.zout.txtr   rx   r   r   
no newliner   r   rO  r)  Nr   r   
zJassert %(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.endswith
}(%(py4)s)
}r   )r&   rm   rY   rL   )r   r   r   rD  r-   rR  endswithr0   r1   r.   r2   r3   r4   r5   )r7   r8   rP  r   r;   r]   rO   rQ   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%%%%%%%%%%w%%%w%%%%%%%%%%%%%%%%	V V	V 	Vs"   ED6E6D?	;EEc                   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
} ||      }|sddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }t        t        j                  |            d	x}x}x}}y	# 1 sw Y   xY w# 1 sw Y   	xY w)uN   결과에 개행 문자가 없어도 stdout 출력 시 개행이 추가된다.r   rx   r   r   re  r   r   rO  Nrf  zcassert %(py8)s
{%(py8)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.out
}.endswith
}(%(py6)s)
}rG  r   )r   r   r   rD  rH  rI  rg  r0   r1   r.   r2   r3   r4   r5   )r7   rJ  rG  r;   r]   rO   rs   rR   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*$T********x***x***|***$***T**********	8 8	8 	8s"   EEEE	EE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                  d	      xs |d
   j                  d	      }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w# 1 sw Y   xY w)u1   --max-tokens 인수가 call_api 에 전달된다.r   rx   r   r   r]  r   )r   rO  z--max-tokens512Nr   rJ   i   r!   r#   r%   r(   r)   ra  )r7   r^  r   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\=]
  zS    zS      z   z   S       O O	O 	Orc  N)rA  z	list[str]r   r   )rJ  pytest.CaptureFixturer   r   )r8   r   rJ  rn  r   r   r   r   )r   r   r   r   rD  rK  rM  rS  rV  rX  r[  r_  rb  rh  rj  rm  r   r   r   r?  r?    sB    5
/
-
-((	(	C"	&+!r   r?  )u   응답 텍스트)r   r-   r   r   ))r   
__future__r   builtinsr0   _pytest.assertion.rewrite	assertionrewriter.   	importlibr   rB  typespathlibr   unittest.mockr   r   r   importlib.utilutil_iluspec_from_file_location_specmodule_from_specr   loaderexec_module_IMPORT_ERROR	Exceptionexc
ModuleTypemarkskipif
pytestmarkr   r   r   r   r   r?  r   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$ $D)DD