
    Ri[                     <   d 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	 ej                  j                  d e ee      j                  j                               ddlmZmZ d*dededz  defd	Zd*dededz  defd
Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      ZddlmZ  G d d      Z G d d      Z G d d      Z G d  d!      Z G d" d#      Z  G d$ d%      Z! G d& d'      Z" G d( d)      Z#y)+u   헤임달(개발2팀 테스터): codex_gate_check 테스트 선행 작성 — TDD RED 단계.

대상: /home/jay/workspace/scripts/codex_gate_check.py
함수: codex_gate_check(task_file, affected_files, workspace_root) -> dict
    N)Path)	MagicMockpatch)codex_gate_check_maat_fallback_checkriskssuggestionsreturnc                 |    | |xs g d}t               }d|_        t        j                  |      |_        d|_        |S )u=   Codex CLI가 정상 반환하는 JSON stdout을 모킹한다.r   r	   r    r   
returncodejsondumpsstdoutstderrr   r	   payload	mock_procs       T/home/jay/workspace/.worktrees/task-2117-dev1/scripts/tests/test_codex_gate_check.py_make_codex_resultr      F     "(bG IIzz'*II    c                 |    | |xs g d}t               }d|_        t        j                  |      |_        d|_        |S )u=   마아트 폴백이 반환하는 JSON stdout을 모킹한다.r   r   r   r   r   s       r   _make_maat_resultr   &   r   r   c                       e Zd ZdZd Zd Zy)TestCodexPassNoCriticalu3   critical severity 리스크가 없으면 gate PASS.c                 
   t        |dz        }t        |      j                  d       dddddddd	dg}t        |d
g      }t	        d|      5  t        |dgd      }d d d        d   du sJ d       y # 1 sw Y   xY w)Ntask.md   # 설계 문서highu   성능 저하 가능성severitydescriptionmediumu   로그 누락 우려lowu   주석 부족u   캐시 레이어 추가 검토r	   subprocess.runreturn_value
src/api.py/home/jay/workspace	task_fileaffected_filesworkspace_rootpassTu!   critical 없으면 PASS여야 함strr   
write_textr   r   r   selftmp_pathr/   r   r   results         r   test_codex_pass_no_criticalz3TestCodexPassNoCritical.test_codex_pass_no_critical9   s    9,-	Y""#45  0IJ!2HI?

 'u;[:\]	#)< 	%# ,~4F	 f~%J'JJ%	 	s   A99Bc                     t        |dz        }t        |      j                  d       t        g g       }t	        d|      5  t        |g d      }ddd       d	   d
u sJ y# 1 sw Y   xY w)u#   리스크가 아예 없어도 PASS.r    u   # 빈 설계r   r)   r*   r-   r.   Nr2   Tr3   r7   r8   r/   r   r9   s        r   test_codex_pass_empty_risksz3TestCodexPassNoCritical.test_codex_pass_empty_risksM   sv    9,-	Y"">2&RR@	#)< 	%#!4F	 f~%%%	 	s   A$$A-N)__name__
__module____qualname____doc__r:   r=    r   r   r   r   6   s    =K(&r   r   c                       e Zd ZdZd Zd Zy)TestCodexFailCriticalExistsu;   critical severity 리스크가 1개 이상이면 gate FAIL.c                     t        |dz        }t        |      j                  d       ddddddg}t        |      }t	        d|	      5  t        |d
gd      }d d d        d   du sJ d       y # 1 sw Y   xY w)Nr    r!   criticalu   인증 우회 취약점r#   r&   u   로그 누락r)   r*   src/auth.pyr-   r.   r2   Fu%   critical 존재 시 FAIL이어야 함r3   r6   s         r   test_codex_fail_single_criticalz;TestCodexFailCriticalExists.test_codex_fail_single_criticald   s    9,-	Y""#45 $4MN!/B
 'u-	#)< 	%# -4F	 f~&O(OO&	 	s   A22A;c                 ,   t        |dz        }t        |      j                  d       ddddddg}t        |      }t	        d|      5  t        |d	d
gd      }ddd       d   du sJ t        d |d   D              }|dk(  sJ y# 1 sw Y   /xY w)u   critical 2개: 여전히 FAIL.r       # 설계rF   u   SQL 인젝션r#   u   권한 상승 가능r)   r*   z	src/db.pyrG   r-   r.   Nr2   Fc              3   2   K   | ]  }|d    dk(  sd  yw)r$   rF      NrB   .0rs     r   	<genexpr>zQTestCodexFailCriticalExists.test_codex_fail_multiple_criticals.<locals>.<genexpr>   s     W11Z=J;VQWs   r      )r4   r   r5   r   r   r   sum)r7   r8   r/   r   r   r9   critical_counts          r   "test_codex_fail_multiple_criticalsz>TestCodexFailCriticalExists.test_codex_fail_multiple_criticalsw   s    9,-	Y"":. $OD#4JK
 'u-	#)< 	%# +];4F	 f~&&&WwWW"""	 	s   B

BN)r>   r?   r@   rA   rH   rT   rB   r   r   rD   rD   a   s    EP&#r   rD   c                       e Zd ZdZd Zd Zy)TestCodexTimeoutFallbackToMaatuU   subprocess.run이 TimeoutExpired를 발생시키면 마아트 폴백을 사용한다.c                     t        |dz        }t        |      j                  d       dddg}t        |dg      fd}t	        d	|
      5  t        |dgd      }d d d        d   dk(  sJ y # 1 sw Y   xY w)Nr    r!   r'   u   폴백 경고r#   u   마아트 제안r(   c                      | r| d   n|j                  dg       }t        d |D              rt        j                  |d      S )Nr   argsc              3   6   K   | ]  }d t        |      v   ywcodexNr4   rN   cs     r   rP   zjTestCodexTimeoutFallbackToMaat.test_codex_timeout_fallback_to_maat.<locals>.side_effect.<locals>.<genexpr>        27c!f$2      cmdtimeoutgetany
subprocessTimeoutExpiredrY   kwargsrd   	maat_procs      r   side_effectzWTestCodexTimeoutFallbackToMaat.test_codex_timeout_fallback_to_maat.<locals>.side_effect   sB    !$q'vzz&"'=C2c22 //CDDr   r)   rn   zsrc/service.pyr-   r.   sourcemaat_fallback)r4   r   r5   r   r   r   )r7   r8   r/   
maat_risksrn   r9   rm   s         @r   #test_codex_timeout_fallback_to_maatzBTestCodexTimeoutFallbackToMaat.test_codex_timeout_fallback_to_maat   s    9,-	Y""#45 $)IJ
%j?Q>RS		 #= 	%# 014F	 h?222	 	s   A33A<c                 $   t        |dz        }t        |      j                  d       t        g g       fd}t	        d|      5  t        |g d      }d	d	d	       h d
}|j                  j                               sJ y	# 1 sw Y   /xY w)uP   타임아웃 폴백 결과도 유효한 딕셔너리 구조를 가져야 한다.r    rJ   r(   c                      | r| d   n|j                  dg       }t        d |D              rt        j                  |d      S )Nr   rY   c              3   6   K   | ]  }d t        |      v   ywr[   r]   r^   s     r   rP   ziTestCodexTimeoutFallbackToMaat.test_codex_timeout_result_is_valid.<locals>.side_effect.<locals>.<genexpr>   r`   ra   rb   rc   rf   rk   s      r   rn   zVTestCodexTimeoutFallbackToMaat.test_codex_timeout_result_is_valid.<locals>.side_effect   sB    !$q'vzz&"'=C2c22 //CDDr   r)   ro   r-   r.   N>   r2   errorr   rp   r	   )r4   r   r5   r   r   r   issubsetkeys)r7   r8   r/   rn   r9   required_keysrm   s         @r   "test_codex_timeout_result_is_validzATestCodexTimeoutFallbackToMaat.test_codex_timeout_result_is_valid   s    9,-	Y"":.%bb9		 #= 	%#!4F	 L%%fkkm444	 	s   	BBN)r>   r?   r@   rA   rs   r{   rB   r   r   rV   rV      s    _305r   rV   c                       e Zd ZdZd Zd Zy)TestCodexApiErrorFallbackToMaatuV   Codex CLI가 비정상 종료(returncode != 0)하면 마아트 폴백을 사용한다.c                    t        |dz        }t        |      j                  d       t               d_        d_        d_        t               d_        t        j                  ddg ii      _        d_        fd	}t        d
|      5  t        |dgd      }d d d        d   dk(  sJ y # 1 sw Y   xY w)Nr    rJ   rL   r   API rate limit exceededr   blast_radiuscallersc                  b    | r| d   n|j                  dg       }t        d |D              rS S )Nr   rY   c              3   6   K   | ]  }d t        |      v   ywr[   r]   r^   s     r   rP   zmTestCodexApiErrorFallbackToMaat.test_codex_api_error_fallback_to_maat.<locals>.side_effect.<locals>.<genexpr>   r`   ra   )rg   rh   )rY   rl   rd   ast_proc
error_procs      r   rn   zZTestCodexApiErrorFallbackToMaat.test_codex_api_error_fallback_to_maat.<locals>.side_effect   s4    !$q'vzz&"'=C2c22!!Or   r)   ro   r,   r-   r.   rp   rq   )r4   r   r5   r   r   r   r   r   r   r   r   )r7   r8   r/   rn   r9   r   r   s        @@r   %test_codex_api_error_fallback_to_maatzETestCodexApiErrorFallbackToMaat.test_codex_api_error_fallback_to_maat   s    9,-	Y"":. [
 !

5
 ;**ny"o%FG	 #= 	%# ,~4F	 h?222	 	s   B66B?c                    t        |dz        }t        |      j                  d       t               }d|_        d|_        d|_        t        d|      5  t        |g d	      }d
d
d
       d   dk(  sJ |d   J y
# 1 sw Y   xY w)uk  API 오류 시 마아트 폴백으로 전환되며 source='maat_fallback'이어야 한다.

        구현체는 returncode != 0이면 즉시 _maat_fallback_check()를 호출한다.
        마아트 폴백 결과의 error 필드는 None으로 설정된다(폴백 자체는 성공).
        대신 source='maat_fallback'으로 오류 전환을 알린다.
        r    rJ   rL   r   zconnection refusedr)   r*   r-   r.   Nrp   rq   rw   	r4   r   r5   r   r   r   r   r   r   r7   r8   r/   r   r9   s        r   %test_codex_api_error_sets_error_fieldzETestCodexApiErrorFallbackToMaat.test_codex_api_error_sets_error_field   s     9,-	Y"":.[
 !

0
 #*= 	%#!4F	 h?222g&&&	 	s   A>>BN)r>   r?   r@   rA   r   r   rB   r   r   r}   r}      s    `3B'r   r}   c                   H    e Zd ZdZh dZd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zy)TestOutputJsonFormatuB   반환 딕셔너리가 명세된 스키마를 충족해야 한다.>   r2   rw   r   rp   r	   c                 h   t        |dz        }t        |      j                  d       t        g       }t	        d|      5  t        |dgd      }d d d        | j                  j                  j                               s%J d| j                  |j                         z
          y # 1 sw Y   XxY w)	Nr    rJ   r)   r*   zsrc/main.pyr-   r.   u   누락 키: )	r4   r   r5   r   r   r   REQUIRED_KEYSrx   ry   r<   s        r   test_all_required_keys_presentz3TestOutputJsonFormat.test_all_required_keys_present  s    9,-	Y"":.&r*	#)< 	%# -4F	 !!**6;;=9n\$J\J\_e_j_j_lJlIm;nn9	 	s   B((B1c                     t        |dz        }t        |      j                  d       t        g       }t	        d|      5  t        |g d      }d d d        t        d   t              sJ y # 1 sw Y   xY w)Nr    rJ   r)   r*   r-   r.   r2   )r4   r   r5   r   r   r   
isinstanceboolr<   s        r   test_pass_is_boolz&TestOutputJsonFormat.test_pass_is_bool  sv    9,-	Y"":.&r*	#)< 	%#!4F	 &.$///	 	s   A..A7c                     t        |dz        }t        |      j                  d       t        dddg      }t	        d|      5  t        |dgd	
      }d d d        t        d   t              sJ y # 1 sw Y   xY w)Nr    rJ   r"   u   경고r#   r)   r*   zsrc/x.pyr-   r.   r   r4   r   r5   r   r   r   r   listr<   s        r   test_risks_is_listz'TestOutputJsonFormat.test_risks_is_list.  s    9,-	Y"":.&VH(U'VW	#)< 	%# *|4F	 &/4000	 	s   A33A<c                     t        |dz        }t        |      j                  d       t        g ddg      }t	        d|      5  t        |g d	      }d d d        t        d
   t              sJ y # 1 sw Y   xY w)Nr    rJ   u   제안 1u   제안 2r(   r)   r*   r-   r.   r	   r   r<   s        r   test_suggestions_is_listz-TestOutputJsonFormat.test_suggestions_is_list=  s~    9,-	Y"":.&r
J7OP	#)< 	%#!4F	 &/666	 	s   A22A;c                     t        |dz        }t        |      j                  d       t        g       }t	        d|      5  t        |g d      }ddd       d   d	k(  sJ y# 1 sw Y   xY w)
u2   Codex 정상 응답 시 source는 codex_companion.r    rJ   r)   r*   r-   r.   Nrp   codex_companionr3   r<   s        r   test_source_is_codex_on_successz4TestOutputJsonFormat.test_source_is_codex_on_successL  sv    9,-	Y"":.&r*	#)< 	%#!4F	 h#4444	 	s   A##A,c                     t        |dz        }t        |      j                  d       t        g       }t	        d|      5  t        |g d      }ddd       d   J y# 1 sw Y   xY w)	u   정상 응답 시 error=None.r    rJ   r)   r*   r-   r.   Nrw   r3   r<   s        r   test_error_is_none_on_successz2TestOutputJsonFormat.test_error_is_none_on_success\  sr    9,-	Y"":.&r*	#)< 	%#!4F	 g&&&	 	s   A  A)c                     t        |dz        }t        |      j                  d       ddddddg}t        |      }t	        d|	      5  t        |d
gd      }ddd       d   D ]  }d|v sJ d       d|v rJ d        y# 1 sw Y   *xY w)uB   리스크 항목은 severity, description 키를 가져야 한다.r    rJ   r"   u   고위험 항목r#   r'   u   저위험 항목r)   r*   zsrc/y.pyr-   r.   Nr   r$   u"   risk 항목에 severity 키 없음r%   u%   risk 항목에 description 키 없음r3   )r7   r8   r/   r   r   r9   items          r   +test_risk_item_has_severity_and_descriptionz@TestOutputJsonFormat.test_risk_item_has_severity_and_descriptionl  s    9,-	Y"":.  0BC/AB
 'u-	#)< 	%# *|4F	 7O 	RD%K'KK% D(Q*QQ(	R	 	s   BBc                 4   t        |dz        }t        |      j                  d       h d}ddddddd	d
ddddg}t        |      }t	        d|      5  t        |dgd      }ddd       d   D ]  }|d   |v rJ d|d            y# 1 sw Y   (xY w)u=   severity는 critical|high|medium|low 중 하나여야 한다.r    rJ   >   r'   r"   r&   rF   rF   u   치명r#   r"   u   높음r&   u   중간r'   u   낮음r)   r*   zsrc/z.pyr-   r.   Nr   r$   u"   허용되지 않는 severity 값: r3   )r7   r8   r/   valid_severitiesr   r   r9   r   s           r   test_severity_values_are_validz3TestOutputJsonFormat.test_severity_values_are_valid  s    9,-	Y"":.@#H=9!(;x8	
 'u-	#)< 	%# *|4F	 7O 	qD
#'77p;]^bcm^n]o9pp7	q	 	s   BBN)r>   r?   r@   rA   r   r   r   r   r   r   r   r   r   rB   r   r   r   r     s5    LGMo0175 ' R,qr   r   c                   D    e Zd ZdZh dZddZd Zd Zd Zd Z	d	 Z
d
 Zy)"TestMaatFallbackReturnsValidResultuV   마아트 폴백 경로에서도 완전한 result 딕셔너리를 반환해야 한다.>   r2   rw   r   rp   r	   Nc           
         t        |dz        }t        |      j                  d       t               }d|_        d|_        d|_        t        d|      5  t        dt        d	 |D               ||xs g d
dd      5  t        |g d      cddd       cddd       S # 1 sw Y   nxY w	 ddd       y# 1 sw Y   yxY w)uL  Codex 오류를 유발하여 마아트 폴백을 강제하는 헬퍼.

        마아트 폴백은 파일 존재 여부를 직접 검사하므로 affected_files는
        실제 존재하는 파일만 넘기거나 빈 목록을 사용한다.
        task_file은 tmp_path 안에 생성하여 실제로 존재하게 한다.
        r    rJ   rL   r   zcodex unavailabler)   r*   z%codex_gate_check._maat_fallback_checkc              3   ,   K   | ]  }|d    dk(    yw)r$   rF   NrB   rM   s     r   rP   zJTestMaatFallbackReturnsValidResult._force_maat_fallback.<locals>.<genexpr>  s     #TAAjMZ$?#Ts   rq   N)r2   r   r	   rp   rw   r-   r.   )
r4   r   r5   r   r   r   r   r   rh   r   )r7   r8   rr   maat_suggestionsr/   r   s         r   _force_maat_fallbackz7TestMaatFallbackReturnsValidResult._force_maat_fallback  s     9,-	Y"":.[
 !

/
#*= 	7 ##T#T TT'#3#9r-!	  ('#%#8 	 	  	 	 	s$   )B5>B	B5B(	$B55B>c                     | j                  |g       }| j                  j                  |j                               sJ y )Nrr   )r   r   rx   ry   r7   r8   r9   s      r   (test_maat_fallback_has_all_required_keyszKTestMaatFallbackReturnsValidResult.test_maat_fallback_has_all_required_keys  s7    **8*C!!**6;;=999r   c                 >    | j                  |g       }|d   dk(  sJ y )Nr   rp   rq   r   r   s      r   *test_maat_fallback_source_is_maat_fallbackzMTestMaatFallbackReturnsValidResult.test_maat_fallback_source_is_maat_fallback  s*    **8*Ch?222r   c                 H    dddg}| j                  ||      }|d   du sJ y)u2   마아트 폴백에서도 critical 없으면 PASS.r"   u   마아트 고위험r#   r   r2   TNr   r7   r8   rr   r9   s       r   -test_maat_fallback_pass_true_when_no_criticalzPTestMaatFallbackReturnsValidResult.test_maat_fallback_pass_true_when_no_critical  s7    #):OPQ
**8
*Kf~%%%r   c                 H    dddg}| j                  ||      }|d   du sJ y)u/   마아트 폴백에서 critical 있으면 FAIL.rF   u   마아트 치명 오류r#   r   r2   FNr   r   s       r   +test_maat_fallback_pass_false_when_criticalzNTestMaatFallbackReturnsValidResult.test_maat_fallback_pass_false_when_critical  s7    #->WXY
**8
*Kf~&&&r   c                 d    ddddddg}| j                  ||      }t        |d         dk(  sJ y	)
uJ   마아트 폴백 리스크 목록이 결과에 그대로 담겨야 한다.r&   u   마아트 중간 위험r#   r'   u   마아트 낮은 위험r   r   rQ   Nr   lenr   s       r   "test_maat_fallback_risks_preservedzETestMaatFallbackReturnsValidResult.test_maat_fallback_risks_preserved  sK     "2KL/HI

 **8
*K6'?#q(((r   c                 ^    g }ddg}| j                  |||      }t        |d         dk(  sJ y)u=   마아트 폴백 제안 목록이 결과에 담겨야 한다.u   마아트 제안 Au   마아트 제안 B)rr   r   r	   rQ   Nr   )r7   r8   rr   r   r9   s        r   (test_maat_fallback_suggestions_preservedzKTestMaatFallbackReturnsValidResult.test_maat_fallback_suggestions_preserved  sC    
02FG**8
]m*n6-()Q...r   N)r>   r?   r@   rA   r   r   r   r   r   r   r   r   rB   r   r   r   r     s.    `GM@:3&')/r   r   c                       e Zd ZdZd Zd Zy)TestCallersContextu?   _get_callers_context가 프롬프트에 통합되는지 검증.c                   	 t        |dz        }t        |      j                  d       |dz  }|j                          |dz  j                  d       t	               d_        t        j                  ddd	gd
dgdgddd      _        d_	        t        g g       	ddig 	fd}t        d|      5  t        |dgt        |            }ddd       d   du sJ t        d D              sJ y# 1 sw Y   'xY w)uI   AST callers 컨텍스트가 Codex 프롬프트에 포함되어야 한다.r    r!   scriptsast_dependency_map.py# dummyr   r,   zserver.py:42zroutes.py:15	server.pyz	routes.pyztest_api.py   )r   direct_importers
test_filestotal_affectedchanged_filer   r   r(   nc                     	dxx   dz  cc<   | r| d   n|j                  dg       }t        d |D              rS t        |      }d|v r|j                  d      }|dz   t	        |      k  r_||dz      }dd l}|j                  j                  |      r8t        |dd	      5 }
j                  |j                                d d d        S S # 1 sw Y   S xY w)
Nr   rL   r   rY   c              3   6   K   | ]  }d t        |      v   yw)ast_dependency_mapNr]   r^   s     r   rP   zbTestCallersContext.test_callers_context_included_in_prompt.<locals>.side_effect.<locals>.<genexpr>  s     ?a'3q61?ra   --prompt-filerO   utf-8encoding)rg   rh   r   indexr   ospathisfileopenappendread)rY   rl   rd   cmd_listidxprompt_pathr   pf
ast_result
call_countcaptured_prompt_filescodex_results           r   rn   zOTestCallersContext.test_callers_context_included_in_prompt.<locals>.side_effect  s    sOq O!$q'vzz&"'=C?3??!!CyH(*nn_57S]*"*37"3Kww~~k2!+sWE D188CD<Ds   " CCr)   ro   r.   Nr2   Tc              3   $   K   | ]  }d |v  
 yw)	   호출됨NrB   )rN   contents     r   rP   zMTestCallersContext.test_callers_context_included_in_prompt.<locals>.<genexpr>)  s     Og;')Os   )r4   r   r5   mkdirr   r   r   r   r   r   r   r   r   rh   )
r7   r8   r/   scripts_dirrn   r9   r   r   r   r   s
         @@@@r   'test_callers_context_included_in_promptz:TestCallersContext.test_callers_context_included_in_prompt  s(   9,-	Y""#45 *	.	.::9E [
 !
 JJ , .?)4k(B#0/&'	!


 
 *""=1X
 "	 $ #= 	%# ,~"8}F	 f~%%%O9NOOOO	 	s   =C<<Dc                     t        |dz        }t        |      j                  d       t        g g       }t	        d|      5  t        |dgd      }d	d	d	       d
v sJ d|v sJ y	# 1 sw Y   xY w)uQ   AST 스크립트 실패 시에도 codex_gate_check는 정상 동작해야 한다.r    r!   r(   r)   r*   znonexistent.py/tmp/nonexistentr.   Nr2   rp   r3   )r7   r8   r/   r   r9   s        r   ,test_callers_context_fallback_on_ast_failurez?TestCallersContext.test_callers_context_fallback_on_ast_failure+  s    9,-	Y""#45 *""=#,? 	%# 011F	 6!!!	 	s   A((A1N)r>   r?   r@   rA   r   r   rB   r   r   r   r     s    I;Pz"r   r   _get_callers_contextc                   "    e Zd ZdZd Zd Zd Zy)TestGetCallersContextu4   _get_callers_context 헬퍼 함수 단위 테스트.c                 ,    t        dgd      }|dk(  sJ y)u3   AST 스크립트가 없으면 빈 문자열 반환.r,   r   r   Nr   )r7   r9   s     r   %test_returns_empty_when_no_ast_scriptz;TestGetCallersContext.test_returns_empty_when_no_ast_scriptH  s    %|n6HI||r   c                    |dz  }|j                          |dz  j                  d       |dz  }|j                  d       t        j                  ddgdgg dd	d
      }t	               }d|_        ||_        d|_        t        d|      5  t        t        |      gt        |            }ddd       dv sJ d|v sJ y# 1 sw Y   xY w)u5   AST 호출 성공 시 호출 관계 문자열 반환.r   r   r   api.pyzdef get_data():
    pass
r   zserver.py:10rQ   )r   r   r   r   r   r   r   r)   r*   Nr   )r   r5   r   r   r   r   r   r   r   r   r4   )r7   r8   r   api_file
ast_outputr   r9   s          r   test_returns_context_on_successz5TestGetCallersContext.test_returns_context_on_successM  s     *	.	.::9E h&9: ZZ ()4 ./"$&'	!


 K	 	%		#)< 	J)3x=/3x=IF	J f$$$'''		J 	Js    C  C	c                    |dz  }|j                          |dz  j                  d       t               }d|_        d|_        d|_        t        d|      5  t        d	gt        |            }d
d
d
       dk(  sJ y
# 1 sw Y   xY w)u+   AST 호출 실패 시 빈 문자열 반환.r   r   r   rL   r   rw   r)   r*   r   N)	r   r5   r   r   r   r   r   r   r4   )r7   r8   r   r   r9   s        r   test_returns_empty_on_ast_errorz5TestGetCallersContext.test_returns_empty_on_ast_errorp  s    *	.	.::9EK	 		"	#)< 	E)8*c(mDF	E ||	E 	Es   A==BN)r>   r?   r@   rA   r   r   r   rB   r   r   r   r   E  s    >
!(Fr   r   c                   "    e Zd ZdZd Zd Zd Zy)TestCodexCascadeuC   3단계 캐스케이드(companion → exec → maat) 동작 검증.c                 "   t        |dz        }t        |      j                  d       t        g       }t	        d|      5  t	        dd      5  t        |g d      }d	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   codex-companion 성공 시 source='codex_companion'.r    rJ   r)   r*   codex_gate_check.os.path.isfileTr-   r.   Nrp   r   r3   r<   s        r   5test_companion_success_returns_codex_companion_sourcezFTestCodexCascade.test_companion_success_returns_codex_companion_source  s    9,-	Y"":.&r*	#)< 	8tL )'K`	
 h#4444	 	 	s$   BA9B9B	>BBc                    t        |dz        }t        |      j                  d       t               }d|_        d|_        d|_        t        d|      5  t        |g d	      }d
d
d
       d   dk(  sJ y
# 1 sw Y   xY w)u3   companion 실패 시 마아트 폴백으로 전환.r    rJ   rL   r   zcompanion errorr)   r*   r-   r.   Nrp   rq   r   r7   r8   r/   	fail_procr9   s        r   &test_companion_fail_falls_back_to_maatz7TestCodexCascade.test_companion_fail_falls_back_to_maat  s    9,-	Y"":.K	 		,	#)< 	%#BG\F	 h?222		 	   A77B c                    t        |dz        }t        |      j                  d       t               }d|_        d|_        d|_        t        d|      5  t        |g d	      }d
d
d
       d   dk(  sJ y
# 1 sw Y   xY w)u.   companion, exec 모두 실패 시 maat 폴백.r    rJ   rL   r   rw   r)   r*   r-   r.   Nrp   rq   r   r   s        r   *test_both_codex_fail_returns_maat_fallbackz;TestCodexCascade.test_both_codex_fail_returns_maat_fallback  s    9,-	Y"":.K	 		"	#)< 	%#BG\F	 h?222		 	r  N)r>   r?   r@   rA   r   r   r  rB   r   r   r   r     s    M
53 3r   r   c                   "    e Zd ZdZd Zd Zd Zy)TestMaatFallbackEnhancedu&   마아트 폴백 강화 기능 검증.c                    t        |dz        }t        |      j                  d       g }t        d      D ]9  }|d| dz  }|j                  d|        |j	                  t        |             ; t        ||t        |            }|d   D cg c]  }|d   d	k(  sd
|d   v s| }}t        |      dk\  sJ yc c}w )u7   affected_files 6개 이상이면 high 리스크 경고.r    r!      filez.pyz# file r   r$   r"   u   변경 범위r%   rL   N)r4   r   r5   ranger   r   r   )	r7   r8   r/   filesifr9   rO   
high_riskss	            r   test_large_scope_warningz1TestMaatFallbackEnhanced.test_large_scope_warning  s    9,-	Y""#45q 	!AT!C=(ALL71#'LLQ 	!
 &iHF!'tAAjMV4KP_cdercsPsat
t:!### us   B<B<%B<c                     t        |dz        }t        |      j                  d       t        |g t        |            }|d   D cg c]  }|d   dk(  sd|d   v s| }}t	        |      dk\  sJ y	c c}w )
uA   task_file에 보안 민감 키워드 포함 시 medium 리스크.r    u@   # 인증 모듈 변경
보안 취약점 수정 및 API키 갱신r   r$   r&   u   보안 민감 키워드r%   rL   Nr4   r   r5   r   r   r7   r8   r/   r9   rO   medium_riskss         r   test_security_keyword_detectionz8TestMaatFallbackEnhanced.test_security_keyword_detection  s    9,-	Y""#fg%iS]C#)'?  Caa
mx6OTmqr  tA  rB  UB  C  C< A%%% Cs   A3A3A3c                     t        |dz        }t        |      j                  d       t        |g t        |            }|d   D cg c]  }|d   dk(  s| }}t	        |      dk(  sJ yc c}w )uH   보안 키워드 없는 일반 task에서는 medium 리스크 미생성.r    u$   # 로깅 개선
로그 포맷 변경r   r$   r&   r   Nr  r  s         r   $test_no_false_positive_on_clean_taskz=TestMaatFallbackEnhanced.test_no_false_positive_on_clean_task  sq    9,-	Y""#JK%iS]C#)'?Paa
mx6OPP< A%%% Qs   A+A+N)r>   r?   r@   rA   r  r  r  rB   r   r   r  r    s    0$&&r   r  c                       e Zd ZdZd Zd Zy)TestApiKeyValidationu'   OPENAI_API_KEY 사전 검증 테스트.c                 &   t        |dz        }t        |      j                  d       t        j                  t
        j                  i d      5  t        |g t        |            }ddd       d   dk(  sJ d	|v sJ |d	   J y# 1 sw Y   !xY w)
uW   OPENAI_API_KEY 없어도 companion 호출을 시도하고, 실패 시 마아트 폴백.r    rJ   T)clearr.   Nrp   rq   fallback_reason)r4   r   r5   r   dictr   environr   r7   r8   r/   r9   s       r   6test_no_api_key_still_attempts_companion_then_fallbackzKTestApiKeyValidation.test_no_api_key_still_attempts_companion_then_fallback  s    9,-	Y"":.ZZ

Bd3 	%#!"8}F	 h?222 F***'(444	 	s   BBc                    t        |dz        }t        |      j                  d       t        g       }t	        j
                  t        j                  ddi      5  t	        d|      5  t	        dd      5  t        |g d	
      }ddd       d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# 1 sw Y   ,xY w)uD   OPENAI_API_KEY가 있으면 companion 호출을 시도해야 한다.r    rJ   OPENAI_API_KEYzsk-test-keyr)   r*   r   Tr-   r.   Nrp   r   	r4   r   r5   r   r   r  r   r  r   r<   s        r   #test_api_key_present_attempts_codexz8TestApiKeyValidation.test_api_key_present_attempts_codex  s    9,-	Y"":.&r*	ZZ

%5}$EF 	'i@ <4P -"+')'<F	 h#4444  	 	s<   C (B46B(B4C (B1-B44B=	9C  C	N)r>   r?   r@   rA   r  r"  rB   r   r   r  r    s    15"5r   r  c                       e Zd ZdZd Zy)TestSubprocessEnvPassingu/   subprocess.run에 env=os.environ 전달 검증.c                    t        |dz        }t        |      j                  d       i fd}t        j                  t
        j                  ddi      5  t        d|      5  t        dd	
      5  ddlm}  |dd       ddd       ddd       ddd       dv sJ dd   v sJ y# 1 sw Y   )xY w# 1 sw Y   -xY w# 1 sw Y   1xY w)uE   _run_codex_companion이 subprocess.run에 env를 전달해야 한다.r    rJ   c                      j                  |       t               }d|_        t        j                  g g d      |_        d|_        |S )Nr   r   r   )updater   r   r   r   r   r   )rY   rl   mockcaptured_kwargss      r   capture_runzJTestSubprocessEnvPassing.test_subprocess_receives_env.<locals>.capture_run  s@    ""6*;DDO**r"%EFDKDKKr   r   sk-testr)   ro   r   Tr*   r   )_run_codex_companionztest promptr-   Nenv)	r4   r   r5   r   r  r   r  r   r,  )r7   r8   r/   r*  r,  r)  s        @r   test_subprocess_receives_envz5TestSubprocessEnvPassing.test_subprocess_receives_env  s    9,-	Y"":.	 ZZ

%5y$AB 	O'[A O<4P OE(8MNOO	O '''?5#9999O OO O	O 	Os<   C%B73B+B7C+B40B77C 	<CCN)r>   r?   r@   rA   r.  rB   r   r   r$  r$  	  s
    9:r   r$  c                   "    e Zd ZdZd Zd Zd Zy)TestFallbackReasonu   폴백 사유 필드 검증.c           	         t        |dz        }t        |      j                  d       t        j                  t
        j                  ddi      5  t        dt        j                  dd      	      5  t        |g t        |      
      }ddd       ddd       d   dk(  sJ d|v sJ d|d   j                         v s
d|d   v sJ yy# 1 sw Y   AxY w# 1 sw Y   ExY w)uI   companion 타임아웃 시 fallback_reason에 타임아웃 사유 포함.r    rJ   r   r+  r)   nodex   rc   ro   r.   Nrp   rq   r  re   u   타임아웃)r4   r   r5   r   r  r   r  ri   rj   r   lowerr  s       r   &test_companion_timeout_includes_reasonz9TestFallbackReason.test_companion_timeout_includes_reason+  s    9,-	Y"":.ZZ

%5y$AB 	'Z5N5NSYcf5gh )'#%#&x=	 h?222 F***F#45;;==SYZkSlAlllAl= 	 	s$   #C2C

C
C	CCc           	         t        |dz        }t        |      j                  d       t               }d|_        d|_        d|_        t        j                  t        j                  ddi      5  t        d|	      5  t        |g t        |      
      }ddd       ddd       d   dk(  sJ d|v sJ y# 1 sw Y   "xY w# 1 sw Y   &xY w)uG   companion 비정상 종료 시 fallback_reason에 에러 사유 포함.r    rJ   rL   r   r   r   r+  r)   r*   r.   Nrp   rq   r  )r4   r   r5   r   r   r   r   r   r  r   r  r   r   s        r   $test_companion_error_includes_reasonz7TestFallbackReason.test_companion_error_includes_reason<  s    9,-	Y"":.[
 !

5
ZZ

%5y$AB 	'jA )'#%#&x=	 h?222 F*** 	 	s$   .C<B5C5B>	:CC
c                    t        |dz        }t        |      j                  d       t        g       }t	        j
                  t        j                  ddi      5  t	        d|      5  t	        dd      5  t        |g d	
      }ddd       ddd       ddd       d   J y# 1 sw Y   !xY w# 1 sw Y   %xY w# 1 sw Y   )xY w)u)   Codex 성공 시 fallback_reason은 None.r    rJ   r   r+  r)   r*   r   Tr-   r.   Nr  r!  r<   s        r   %test_codex_success_no_fallback_reasonz8TestFallbackReason.test_codex_success_no_fallback_reasonQ  s    9,-	Y"":.&r*	ZZ

%5y$AB 	'i@ <4P -"+')'<F	 '(000  	 	s<   B=(B16B%B1B=%B.*B11B:	6B==CN)r>   r?   r@   rA   r5  r7  r9  rB   r   r   r0  r0  (  s    &m"+*1r   r0  c                   "    e Zd ZdZd Zd Zd Zy)TestSanitizeGateIntegrationu4   Codex 호출 전 PII 마스킹 자동 적용 검증.c           	      h   t        |dz        }t        |      j                  d       g fd}t        d|      5  t        dd      5  t	        |g t        |      	      }d
d
d
       d
d
d
       d   du sJ rd   }d|vsJ d       d|vsJ d       y
y
# 1 sw Y   9xY w# 1 sw Y   =xY w)uP   프롬프트에 PII(전화번호)가 포함되면 마스킹 후 Codex에 전달.r    u>   # 설계
연락처: 010-1234-5678
주민번호: 900101-1234567c                     | r| d   n|j                  dg       }t        |      }d|v r|j                  d      }|dz   t        |      k  r]||dz      }t        j
                  j                  |      r6t        |dd      5 }j                  |j                                d d d        t               }d|_        t        j                  g g d      |_        d	|_        |S # 1 sw Y   @xY w)
Nr   rY   r   rL   rO   r   r   r   r   )rg   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )	rY   rl   rd   r   r   r   r   r(  captured_promptss	           r   r*  z_TestSanitizeGateIntegration.test_pii_in_prompt_is_masked_before_codex_call.<locals>.capture_runq  s    !$q'vzz&"'=CCyH(*nn_57S]*"*37"3Kww~~k2!+sWE ?,33BGGI>?;DDO**r"%EFDKDKK? ?s    C  C)r)   ro   r   Tr*   r.   Nr2   r   z010-1234-5678u)   전화번호가 마스킹되지 않았음z900101-1234567u)   주민번호가 마스킹되지 않았음)r4   r   r5   r   r   )r7   r8   r/   r*  r9   prompt_textr>  s         @r   .test_pii_in_prompt_is_masked_before_codex_callzJTestSanitizeGateIntegration.test_pii_in_prompt_is_masked_before_codex_callj  s    9,-	Y""#ef	  #= 	8tL )'#%#&x=	 f~%%%*1-K"+5b7bb5#;6c8cc6  	 	s#   B(B$B(B%	!B((B1c                 v   t        |dz        }t        |      j                  d       t        g       }t	        dd      5  t	        d|      5  t	        dd      5  t        |g d	
      }ddd       ddd       ddd       d   du sJ |d   dk(  sJ y# 1 sw Y   -xY w# 1 sw Y   1xY w# 1 sw Y   5xY w)uD   sanitize_gate import 실패해도 codex_gate_check는 정상 동작.r    u!   # 설계
연락처: 010-1234-5678z$codex_gate_check._SANITIZE_AVAILABLEFr)   r*   r   Tr-   r.   Nr2   rp   r   r3   r<   s        r   %test_sanitize_unavailable_still_workszATestSanitizeGateIntegration.test_sanitize_unavailable_still_works  s    9,-	Y""#GH&r*	95A 	'i@ <4P -"+')'<F	 f~%%%h#4444  	 	s<    B/B#B+B#3B/B B##B,	(B//B8c                     t        |dz        }t        |      j                  d       t        g       }t	        d|      5  t	        dd      5  t        |g d      }d	d	d	       d	d	d	       d
   du sJ y	# 1 sw Y   xY w# 1 sw Y   xY w)u7   PII 없는 프롬프트에서는 마스킹 감지 0건.r    u+   # 일반 설계 문서
로깅 개선 작업r)   r*   r   Tr-   r.   Nr2   r3   r<   s        r   test_no_pii_no_masking_logz6TestSanitizeGateIntegration.test_no_pii_no_masking_log  s    9,-	Y""#QR&r*	#)< 	8tL )'#%#8	 f~%%% 	 	s$   BA8B8B	=BBN)r>   r?   r@   rA   r@  rB  rD  rB   r   r   r;  r;  g  s    >$dL5&&r   r;  c                   "    e Zd ZdZd Zd Zd Zy)TestGateFileAutoGenerationud   codex_gate_check()가 task_id 전달 시 .codex-gate 결과 파일을 자동 생성하는지 검증.c           	         t        |dz        }t        |      j                  d       |dz  dz  }|j                  d       t	        g       }t        d|      5  t        d	d      5  t        |g t        |      d
      }ddd       ddd       |dz  }|j                         sJ d       t        j                  |j                               }|d   d
k(  sJ d|v sJ |d   d   k(  sJ y# 1 sw Y   nxY w# 1 sw Y   rxY w)u9   Codex 성공 시 결과 파일이 생성되어야 한다.r    rJ   memoryeventsTparentsr)   r*   r   ztask-test-001r/   r0   r1   task_idNztask-test-001.codex-gateu$   결과 파일이 생성되어야 함rM  	timestampr2   )r4   r   r5   r   r   r   r   existsr   loads	read_text)r7   r8   r/   
events_dirr   r9   	gate_filedatas           r   'test_gate_file_created_on_codex_successzBTestGateFileAutoGeneration.test_gate_file_created_on_codex_success  s   9,-	Y"":.(83
&&r*	#)< 	8tL )'#%#&x=+		 !;;	!I#II!zz)--/0I/111d"""F|vf~--- 	 	s$   C;)C/C;/C8	4C;;Dc                    t        |dz        }t        |      j                  d       |dz  dz  }|j                  d       t	               }d|_        d|_        d	|_        t        d
|      5  t        |g t        |      d       ddd       |dz  }|j                         sJ d       t        j                  |j                               }|d   dk(  sJ y# 1 sw Y   SxY w)uC   마아트 폴백 시에도 결과 파일이 생성되어야 한다.r    rJ   rH  rI  TrJ  rL   r   rw   r)   r*   ztask-test-002rL  Nztask-test-002.codex-gateu5   폴백 시에도 결과 파일이 생성되어야 함rp   rq   )r4   r   r5   r   r   r   r   r   r   r   rO  r   rP  rQ  )r7   r8   r/   rR  r   rS  rT  s          r   'test_gate_file_created_on_maat_fallbackzBTestGateFileAutoGeneration.test_gate_file_created_on_maat_fallback  s    9,-	Y"":.(83
&[
 !

#
#*= 	#!"8}'		 !;;	!Z#ZZ!zz)--/0H~000	 	s   /CC#c                 n   t        |dz        }t        |      j                  d       |dz  dz  }|j                  d       t	        g       }t        d|      5  t        |g t        |      	       d
d
d
       t        |j                  d            }t        |      dk(  sJ d       y
# 1 sw Y   9xY w)uA   task_id 없으면 결과 파일이 생성되지 않아야 한다.r    rJ   rH  rI  TrJ  r)   r*   r.   Nz*.codex-gater   u)   task_id 없으면 결과 파일 미생성)
r4   r   r5   r   r   r   r   r   globr   )r7   r8   r/   rR  r   
gate_filess         r   !test_no_gate_file_without_task_idz<TestGateFileAutoGeneration.test_no_gate_file_without_task_id  s    9,-	Y"":.(83
&&r*	#)< 	#!"8}	 *//.9:
:!#P%PP#	 	s   B++B4N)r>   r?   r@   rA   rU  rW  r[  rB   r   r   rF  rF    s    n.012Qr   rF  r   )$rA   r   r   ri   syspathlibr   unittest.mockr   r   r   insertr4   __file__parentr   r   r   r   r   r   rD   rV   r}   r   r   r   r   r   r   r  r  r$  r0  r;  rF  rB   r   r   <module>rb     s<    	  
  * 3tH~,,334 5 C
d 
 
	 

T 
t 
y 
 %& %&V*# *#`05 05l>' >'HMq MqfG/ G/ZQ" Q"n 29 9~-3 -3f"& "&P$5 $5T: :>91 91~K& K&bDQ DQr   