
    i                        d 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 e
j                   j#                  d e ee      j(                  j(                               ddlmZ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* 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_normalize_affected_item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       :/home/jay/workspace/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 }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	t        j                  d      dz   d|	iz  }
t        t        j                  |
            d x}x}}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passTisz%(py1)s is %(py4)spy1py4u!   critical 없으면 PASS여야 함
>assert %(py6)spy6strr   
write_textr   r   r   
@pytest_ar_call_reprcompare	_saferepr_format_assertmsgAssertionError_format_explanationselftmp_pathr0   r	   r   result@py_assert0@py_assert3@py_assert2@py_format5@py_format7s              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JJ~JJJ~JJJJJJ'JJJJJJJJ	 	s   D  D	c                    t        |dz        }t        |      j                  d       t        g g       }t	        d|      5  t        |g d      }ddd       d	   }d
}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y# 1 sw Y   xY w)u#   리스크가 아예 없어도 PASS.r!   u   # 빈 설계r   r*   r+   r.   r/   Nr3   Tr4   r6   r7   assert %(py6)sr;   r=   r   r>   r   r   r   r?   r@   rA   rC   rD   
rF   rG   r0   r   rH   rI   rJ   rK   rL   rM   s
             r   test_codex_pass_empty_risksz3TestCodexPassNoCritical.test_codex_pass_empty_risksM   s    9,-	Y"">2&RR@	#)< 	%#!4F	 f~%%~%%%%~%%%~%%%%%%%%%%	 	s   CC#N)__name__
__module____qualname____doc__rN   rS    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 }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	t        j                  d      dz   d|	iz  }
t        t        j                  |
            d x}x}}y # 1 sw Y   xY w)Nr!   r"   criticalu   인증 우회 취약점r$   r'   u   로그 누락r*   r+   src/auth.pyr.   r/   r3   Fr4   r6   r7   u%   critical 존재 시 FAIL이어야 함r:   r;   r<   rE   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OO~OOO~OOOOOO(OOOOOOOO	 	s   C99Dc                 X   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 }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}t        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   ExY w)u   critical 2개: 여전히 FAIL.r!      # 설계r\   u   SQL 인젝션r$   u   권한 상승 가능r*   r+   z	src/db.pyr]   r.   r/   Nr3   Fr4   r6   r7   rP   r;   c              3   2   K   | ]  }|d    dk(  sd  yw)r%   r\      NrX   .0rs     r   	<genexpr>zQTestCodexFailCriticalExists.test_codex_fail_multiple_criticals.<locals>.<genexpr>   s     W11Z=J;VQWs   r	      ==z%(py0)s == %(py3)scritical_countpy0py3assert %(py5)spy5)r=   r   r>   r   r   r   r?   r@   rA   rC   rD   sum@py_builtinslocals_should_repr_global_name)rF   rG   r0   r	   r   rH   rI   rJ   rK   rL   rM   rk   @py_assert1@py_format4@py_format6s                  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   FF)N)rT   rU   rV   rW   r^   rx   rX   r   r   rZ   rZ   a   s    EP&#r   rZ   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(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            d x}x}}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r=   rd   cs     r   rf   zjTestCodexTimeoutFallbackToMaat.test_codex_timeout_fallback_to_maat.<locals>.side_effect.<locals>.<genexpr>        27c!f$2      cmdtimeoutgetany
subprocessTimeoutExpiredr}   kwargsr   	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*   r   zsrc/service.pyr.   r/   sourcemaat_fallbackrh   z%(py1)s == %(py4)sr7   rP   r;   )r=   r   r>   r   r   r   r?   r@   rA   rC   rD   )rF   rG   r0   
maat_risksr   rH   rI   rJ   rK   rL   rM   r   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222?222222?2222222	 	s   C))C2c           	      2   t        |dz        }t        |      j                  d       t        g g       fd}t	        d|      5  t        |g d      }d	d	d	       h d
}|j                  }j                  } |       } ||      }	|	sd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t        j                  |      t        j                  |      t        j                  |	      dz  }
t        t        j                  |
            d	x}x}x}}	y	# 1 sw Y   6xY w)uP   타임아웃 폴백 결과도 유효한 딕셔너리 구조를 가져야 한다.r!   r`   r)   c                      | r| d   n|j                  dg       }t        d |D              rt        j                  |d      S )Nr   r}   c              3   6   K   | ]  }d t        |      v   ywr   r   r   s     r   rf   ziTestCodexTimeoutFallbackToMaat.test_codex_timeout_result_is_valid.<locals>.side_effect.<locals>.<genexpr>   r   r   r   r   r   r   s      r   r   zVTestCodexTimeoutFallbackToMaat.test_codex_timeout_result_is_valid.<locals>.side_effect   sB    !$q'vzz&"'=C2c22 //CDDr   r*   r   r.   r/   N>   r3   errorr	   r   r
   z{assert %(py9)s
{%(py9)s = %(py2)s
{%(py2)s = %(py0)s.issubset
}(%(py7)s
{%(py7)s = %(py5)s
{%(py5)s = %(py3)s.keys
}()
})
}required_keysrH   )rm   py2rn   rp   py7py9)r=   r   r>   r   r   r   issubsetkeysrr   rs   r?   rt   rA   rC   rD   )rF   rG   r0   r   rH   r   ru   @py_assert4@py_assert6@py_assert8@py_format10r   s              @r   "test_codex_timeout_result_is_validzATestCodexTimeoutFallbackToMaat.test_codex_timeout_result_is_valid   s   9,-	Y"":.%bb9		 #= 	%#!4F	 L%%4fkk4km4%m44444444}444}444%444444f444f444k444m4444444444	 	s   	FFN)rT   rU   rV   rW   r   r   rX   r   r   rz   rz      s    _305r   rz   c                       e Zd ZdZd Zd Zy)TestCodexApiErrorFallbackToMaatuV   Codex CLI가 비정상 종료(returncode != 0)하면 마아트 폴백을 사용한다.c                 p  
 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(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d x}x}}y # 1 sw Y   xY w)Nr!   r`   rb   r   API rate limit exceededr   blast_radiuscallersc                  b    | r| d   n|j                  dg       }t        d |D              rS S )Nr   r}   c              3   6   K   | ]  }d t        |      v   ywr   r   r   s     r   rf   zmTestCodexApiErrorFallbackToMaat.test_codex_api_error_fallback_to_maat.<locals>.side_effect.<locals>.<genexpr>   r   r   )r   r   )r}   r   r   ast_proc
error_procs      r   r   zZTestCodexApiErrorFallbackToMaat.test_codex_api_error_fallback_to_maat.<locals>.side_effect   s4    !$q'vzz&"'=C2c22!!Or   r*   r   r-   r.   r/   r   r   rh   r   r7   rP   r;   )r=   r   r>   r   r   r   r   r   r   r   r   r?   r@   rA   rC   rD   )rF   rG   r0   r   rH   rI   rJ   rK   rL   rM   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222?222222?2222222	 	s   D,,D5c                    t        |dz        }t        |      j                  d       t               }d|_        d|_        d|_        t        d|      5  t        |g d	      }d
d
d
       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
}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d
x}x}}y
# 1 sw Y   
xY w)uk  API 오류 시 마아트 폴백으로 전환되며 source='maat_fallback'이어야 한다.

        구현체는 returncode != 0이면 즉시 _maat_fallback_check()를 호출한다.
        마아트 폴백 결과의 error 필드는 None으로 설정된다(폴백 자체는 성공).
        대신 source='maat_fallback'으로 오류 전환을 알린다.
        r!   r`   rb   r   zconnection refusedr*   r+   r.   r/   Nr   r   rh   r   r7   rP   r;   r   r4   r6   r=   r   r>   r   r   r   r   r   r   r?   r@   rA   rC   rD   )
rF   rG   r0   r   rH   rI   rJ   rK   rL   rM   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222?222222?2222222g&$&$&&&&$&&&&&&$&&&&&&&	 	s   E,,E6N)rT   rU   rV   rW   r   r   rX   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   반환 딕셔너리가 명세된 스키마를 충족해야 한다.>   r3   r   r	   r   r
   c           
         t        |dz        }t        |      j                  d       t        g       }t	        d|      5  t        |dgd      }d d d        | j                  }|j                  }j                  } |       } ||      }	|	s>t        j                  d| j                  |j                         z
         d	z   d
t        j                         v st        j                  |       rt        j                  |       nd
t        j                  |      t        j                  |      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}x}x}}	y # 1 sw Y   xY w)Nr!   r`   r*   r+   zsrc/main.pyr.   r/   u   누락 키: z
>assert %(py11)s
{%(py11)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.REQUIRED_KEYS
}.issubset
}(%(py9)s
{%(py9)s = %(py7)s
{%(py7)s = %(py5)s.keys
}()
})
}rF   rH   rm   r   r9   rp   r   r   py11)r=   r   r>   r   r   r   REQUIRED_KEYSr   r   r?   rB   rr   rs   rt   rA   rC   rD   )rF   rG   r0   r   rH   ru   rJ   r   r   @py_assert10@py_format12s              r   test_all_required_keys_presentz3TestOutputJsonFormat.test_all_required_keys_present  sL   9,-	Y"":.&r*	#)< 	%# -4F	 !!n!**n6;;n;=n*=9n9nn\$J\J\_e_j_j_lJlIm;nnnnnnntnnntnnn!nnn*nnnnnn6nnn6nnn;nnn=nnn9nnnnnnn	 	s   GG"c                    t        |dz        }t        |      j                  d       t        g       }t	        d|      5  t        |g d      }d d d        d   }t        |t              }|sdd	t        j                         v st        j                  t              rt        j                  t              nd	t        j                  |      d
t        j                         v st        j                  t              rt        j                  t              nd
t        j                  |      dz  }t        t        j                  |            d x}}y # 1 sw Y   xY w)Nr!   r`   r*   r+   r.   r/   r3   5assert %(py5)s
{%(py5)s = %(py0)s(%(py2)s, %(py3)s)
}
isinstanceboolrm   r   rn   rp   )r=   r   r>   r   r   r   r   r   rr   rs   r?   rt   rA   rC   rD   rF   rG   r0   r   rH   ru   r   rw   s           r   test_pass_is_boolz&TestOutputJsonFormat.test_pass_is_bool  s    9,-	Y"":.&r*	#)< 	%#!4F	 !./z.$////////z///z///.//////$///$//////////	 	s   EEc                    t        |dz        }t        |      j                  d       t        dddg      }t	        d|      5  t        |dgd	
      }d d d        d   }t        |t              }|sddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            d x}}y # 1 sw Y   xY w)Nr!   r`   r#   u   경고r$   r*   r+   zsrc/x.pyr.   r/   r	   r   r   listr   r=   r   r>   r   r   r   r   r   rr   rs   r?   rt   rA   rC   rD   r   s           r   test_risks_is_listz'TestOutputJsonFormat.test_risks_is_list.  s    9,-	Y"":.&VH(U'VW	#)< 	%# *|4F	 !/0z/400000000z000z000/000000400040000000000	 	s   EE!c                    t        |dz        }t        |      j                  d       t        g ddg      }t	        d|      5  t        |g d	      }d d d        d
   }t        |t              }|sddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            d x}}y # 1 sw Y   xY w)Nr!   r`   u   제안 1u   제안 2r)   r*   r+   r.   r/   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	 !/6z/66666666z666z666/6666666666666666666	 	s   EE c                    t        |dz        }t        |      j                  d       t        g       }t	        d|      5  t        |g d      }ddd       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# 1 sw Y   xY w)u2   Codex 정상 응답 시 source는 codex_companion.r!   r`   r*   r+   r.   r/   Nr   codex_companionrh   r   r7   rP   r;   rQ   rR   s
             r   test_source_is_codex_on_successz4TestOutputJsonFormat.test_source_is_codex_on_successL  s    9,-	Y"":.&r*	#)< 	%#!4F	 h4#44#44444#4444444#44444444	 	s   CC"c                    t        |dz        }t        |      j                  d       t        g       }t	        d|      5  t        |g d      }ddd       d   }d}||u }|slt        j                  d	|fd
||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y# 1 sw Y   xY w)u   정상 응답 시 error=None.r!   r`   r*   r+   r.   r/   Nr   r4   r6   r7   rP   r;   rQ   rR   s
             r   test_error_is_none_on_successz2TestOutputJsonFormat.test_error_is_none_on_success\  s    9,-	Y"":.&r*	#)< 	%#!4F	 g&$&$&&&&$&&&&&&$&&&&&&&	 	s   CC!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 ]y  }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }	t        j                  d      dz   d|	iz  }
t        t        j                  |
            dx}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }	t        j                  d      dz   d|	iz  }
t        t        j                  |
            dx}}| y# 1 sw Y   xY w)uB   리스크 항목은 severity, description 키를 가져야 한다.r!   r`   r#   u   고위험 항목r$   r(   u   저위험 항목r*   r+   zsrc/y.pyr.   r/   Nr	   r%   inz%(py1)s in %(py3)sitemr8   rn   u"   risk 항목에 severity 키 없음
>assert %(py5)srp   r&   u%   risk 항목에 description 키 없음r=   r   r>   r   r   r   r?   r@   rA   rr   rs   rt   rB   rC   rD   )rF   rG   r0   r	   r   rH   r   rI   rK   rv   rw   s              r   +test_risk_item_has_severity_and_descriptionz@TestOutputJsonFormat.test_risk_item_has_severity_and_descriptionl  sc   9,-	Y"":.  0BC/AB
 'u-	#)< 	%# *|4F	 7O 	RDK:%KKK:KKK:KKKKKKKKKKKKK'KKKKKKK Q=D(QQQ=DQQQ=QQQQQQDQQQDQQQQ*QQQQQQQQ	R	 	s   G&&G0c                    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 }	|	st        j                  d|	fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }
t        j                  d|d          dz   d|
iz  }t        t        j                  |            dx}}	 y# 1 sw Y   xY w)u=   severity는 critical|high|medium|low 중 하나여야 한다.r!   r`   >   r(   r#   r'   r\   r\   u   치명r$   r#   u   높음r'   u   중간r(   u   낮음r*   r+   zsrc/z.pyr.   r/   Nr	   r%   r   r   valid_severitiesr   u"   허용되지 않는 severity 값: r   rp   r   )rF   rG   r0   r   r	   r   rH   r   rI   rK   rv   rw   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
#p#'77ppp#'7ppp#pppppp'7ppp'7pppp;]^bcm^n]o9ppppppp	q	 	s   D>>EN)rT   rU   rV   rW   r   r   r   r   r   r   r   r   r   rX   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 딕셔너리를 반환해야 한다.>   r3   r   r	   r   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!   r`   rb   r   zcodex unavailabler*   r+   z%codex_gate_check._maat_fallback_checkc              3   ,   K   | ]  }|d    dk(    yw)r%   r\   NrX   rc   s     r   rf   zJTestMaatFallbackReturnsValidResult._force_maat_fallback.<locals>.<genexpr>  s     #TAAjMZ$?#Ts   r   N)r3   r	   r
   r   r   r.   r/   )
r=   r   r>   r   r   r   r   r   r   r   )rF   rG   r   maat_suggestionsr0   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
ddt	        j
                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      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}x}x}}y )Nr   zassert %(py11)s
{%(py11)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.REQUIRED_KEYS
}.issubset
}(%(py9)s
{%(py9)s = %(py7)s
{%(py7)s = %(py5)s.keys
}()
})
}rF   rH   r   )r   r   r   r   rr   rs   r?   rt   rA   rC   rD   )	rF   rG   rH   ru   rJ   r   r   r   r   s	            r   (test_maat_fallback_has_all_required_keyszKTestMaatFallbackReturnsValidResult.test_maat_fallback_has_all_required_keys  s    **8*C!!9!**96;;9;=9*=99999999t999t999!999*99999969996999;999=99999999999r   c                 *   | j                  |g       }|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 )	Nr   r   r   rh   r   r7   rP   r;   r   r?   r@   rA   rC   rD   )rF   rG   rH   rI   rJ   rK   rL   rM   s           r   *test_maat_fallback_source_is_maat_fallbackzMTestMaatFallbackReturnsValidResult.test_maat_fallback_source_is_maat_fallback  si    **8*Ch2?2?2222?222222?2222222r   c                 4   dddg}| j                  ||      }|d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }d
d|iz  }t	        t        j
                  |            dx}x}}y)u2   마아트 폴백에서도 critical 없으면 PASS.r#   u   마아트 고위험r$   r   r3   Tr4   r6   r7   rP   r;   Nr   	rF   rG   r   rH   rI   rJ   rK   rL   rM   s	            r   -test_maat_fallback_pass_true_when_no_criticalzPTestMaatFallbackReturnsValidResult.test_maat_fallback_pass_true_when_no_critical  ss    #):OPQ
**8
*Kf~%%~%%%%~%%%~%%%%%%%%%%r   c                 4   dddg}| j                  ||      }|d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }d
d|iz  }t	        t        j
                  |            dx}x}}y)u/   마아트 폴백에서 critical 있으면 FAIL.r\   u   마아트 치명 오류r$   r   r3   Fr4   r6   r7   rP   r;   Nr   r   s	            r   +test_maat_fallback_pass_false_when_criticalzNTestMaatFallbackReturnsValidResult.test_maat_fallback_pass_false_when_critical  ss    #->WXY
**8
*Kf~&&~&&&&~&&&~&&&&&&&&&&r   c                    ddddddg}| j                  ||      }|d   }t        |      }d}||k(  }|st        j                  d	|fd
||f      dt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}x}}y)uJ   마아트 폴백 리스크 목록이 결과에 그대로 담겨야 한다.r'   u   마아트 중간 위험r$   r(   u   마아트 낮은 위험r   r	   rg   rh   z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)slenrm   r   r9   r   assert %(py9)sr   N
r   r   r?   r@   rr   rs   rt   rA   rC   rD   )
rF   rG   r   rH   ru   rJ   r   @py_assert5@py_format8r   s
             r   "test_maat_fallback_risks_preservedzETestMaatFallbackReturnsValidResult.test_maat_fallback_risks_preserved  s     "2KL/HI

 **8
*K'?(s?#(q(#q((((#q((((((s(((s(((?(((#(((q(((((((r   c                    g }ddg}| j                  |||      }|d   }t        |      }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      t        j                  |      d	z  }	d
d|	iz  }
t        t        j                  |
            dx}x}x}}y)u=   마아트 폴백 제안 목록이 결과에 담겨야 한다.u   마아트 제안 Au   마아트 제안 B)r   r   r
   rg   rh   r   r   r   r   r   Nr   )rF   rG   r   r   rH   ru   rJ   r   r   r   r   s              r   (test_maat_fallback_suggestions_preservedzKTestMaatFallbackReturnsValidResult.test_maat_fallback_suggestions_preserved  s    
02FG**8
]m*n-(.s().Q.)Q....)Q......s...s...(...)...Q.......r   N)rT   rU   rV   rW   r   r   r   r   r   r   r   r   rX   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                 4   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 }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t!        t        j"                  |
            dx}x}}d D        }t%        |      }|sd d!t'        j(                         v st        j*                  t$              rt        j                  t$              nd!t        j                  |      t        j                  |      d"z  }	t!        t        j"                  |	            dx}}y# 1 sw Y   9x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  rb   r   r}   c              3   6   K   | ]  }d t        |      v   yw)ast_dependency_mapNr   r   s     r   rf   zbTestCallersContext.test_callers_context_included_in_prompt.<locals>.side_effect.<locals>.<genexpr>  s     ?a'3q61?r   --prompt-filere   utf-8encoding)r   r   r   indexr   ospathisfileopenappendread)r}   r   r   cmd_listidxprompt_pathr  pf
ast_result
call_countcaptured_prompt_filescodex_results           r   r   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*   r   r/   Nr3   Tr4   r6   r7   rP   r;   c              3   $   K   | ]  }d |v  
 yw)	   호출됨NrX   )rd   contents     r   rf   zMTestCallersContext.test_callers_context_included_in_prompt.<locals>.<genexpr>)  s     Og;')Os   z,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}r   rm   r   r9   )r=   r   r>   mkdirr   r   r   r   r   r   r   r   r   r?   r@   rA   rC   rD   r   rr   rs   rt   )rF   rG   r0   scripts_dirr   rH   rI   rJ   rK   rL   rM   ru   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OsOOOOOOOOOsOOOsOOOOOOOOOOOOOO	 	s   =HHc                 f   t        |dz        }t        |      j                  d       t        g g       }t	        d|      5  t        |dgd      }d	d	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}}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   UxY w)uQ   AST 스크립트 실패 시에도 codex_gate_check는 정상 동작해야 한다.r!   r"   r)   r*   r+   znonexistent.py/tmp/nonexistentr/   Nr3   r   r   rH   r   ro   rp   r   )r=   r   r>   r   r   r   r?   r@   rA   rr   rs   rt   rC   rD   )	rF   rG   r0   r  rH   rI   rK   rv   rw   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	 vvv!x6!!!!x6!!!x!!!!!!6!!!6!!!!!!!	 	s   F&&F0N)rT   rU   rV   rW   r$  r'  rX   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                 j   t        dg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
)u3   AST 스크립트가 없으면 빈 문자열 반환.r-   r&  r   rh   rj   rH   rl   ro   rp   N)	r(  r?   r@   rr   rs   rt   rA   rC   rD   )rF   rH   rK   ru   rv   rw   s         r   %test_returns_empty_when_no_ast_scriptz;TestGetCallersContext.test_returns_empty_when_no_ast_scriptH  si    %|n6HIv|vvv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 }|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}}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   UxY w)u5   AST 호출 성공 시 호출 관계 문자열 반환.r   r   r   api.pyzdef get_data():
    pass
r   zserver.py:10rg   )r  r   r  r  r  r   r   r*   r+   Nr  r   r   rH   r   ro   rp   )r"  r>   r   r   r   r   r   r   r   r(  r=   r?   r@   rA   rr   rs   rt   rC   rD   )rF   rG   r#  api_file
ast_outputr   rH   rI   rK   rv   rw   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$$$${f$$${$$$$$$f$$$f$$$$$$$'~''''~'''~''''''''''''''''		J 	Js    G>>Hc                 P   |dz  }|j                          |dz  j                  d       t               }d|_        d|_        d|_        t        d|      5  t        d	gt        |            }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+   AST 호출 실패 시 빈 문자열 반환.r   r   r   rb   r   r   r*   r+   r.  Nrh   rj   rH   rl   ro   rp   )r"  r>   r   r   r   r   r   r(  r=   r?   r@   rr   rs   rt   rA   rC   rD   )	rF   rG   r#  r   rH   rK   ru   rv   rw   s	            r   test_returns_empty_on_ast_errorz5TestGetCallersContext.test_returns_empty_on_ast_errorp  s    *	.	.::9EK	 		"	#)< 	E)8*c(mDF	E v|vvv	E 	Es   DD%N)rT   rU   rV   rW   r,  r1  r3  rX   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(  }|slt        j                  d|fd||f      t        j                  |      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)u4   codex-companion 성공 시 source='codex_companion'.r!   r`   r*   r+   codex_gate_check.os.path.isfileTr.   r/   Nr   r   rh   r   r7   rP   r;   rQ   rR   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4444#4444444#44444444	 	 	s$   C;C/C;/C8	4C;;Dc                    t        |dz        }t        |      j                  d       t               }d|_        d|_        d|_        t        d|      5  t        |g d	      }d
d
d
       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
# 1 sw Y   xY w)u3   companion 실패 시 마아트 폴백으로 전환.r!   r`   rb   r   zcompanion errorr*   r+   r.   r/   Nr   r   rh   r   r7   rP   r;   r   
rF   rG   r0   	fail_procrH   rI   rJ   rK   rL   rM   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222?222222?2222222		 	   C--C6c                    t        |dz        }t        |      j                  d       t               }d|_        d|_        d|_        t        d|      5  t        |g d	      }d
d
d
       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
# 1 sw Y   xY w)u.   companion, exec 모두 실패 시 maat 폴백.r!   r`   rb   r   r   r*   r+   r.   r/   Nr   r   rh   r   r7   rP   r;   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222?222222?2222222		 	r=  N)rT   rU   rV   rW   r8  r<  r?  rX   r   r   r5  r5    s    M
53 3r   r5  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\  }|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c c}w )u7   affected_files 6개 이상이면 high 리스크 경고.r!   r"      filez.pyz# file r	   r%   r#   u   변경 범위r&   rb   >=z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} >= %(py6)sr   
high_risksrm   r8   rn   r;   assert %(py8)spy8N)r=   r   r>   ranger  r   r   r?   r@   rr   rs   rt   rA   rC   rD   )rF   rG   r0   filesifrH   re   rH  rK   r   r   rM   @py_format9s                 r   test_large_scope_warningz1TestMaatFallbackEnhanced.test_large_scope_warning  sG   9,-	Y""#45q 	!AT!C=(ALL71#'LLQ 	!
 &iHF!'tAAjMV4KP_cdercsPsat
t:#!#!####!######s###s######:###:######!####### us   F<F<%F<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\  }|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c c}w )uA   task_file에 보안 민감 키워드 포함 시 medium 리스크.r!   u@   # 인증 모듈 변경
보안 취약점 수정 및 API키 갱신r	   r%   r'   u   보안 민감 키워드r&   rb   rE  rG  r   medium_risksrI  rJ  rK  Nr=   r   r>   r   r   r?   r@   rr   rs   rt   rA   rC   rD   rF   rG   r0   rH   re   rS  rK   r   r   rM   rP  s              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% A%%%% A%%%%%%s%%%s%%%%%%<%%%<%%% %%%A%%%%%%% Cs   E3E3E3c                    t        |dz        }t        |      j                  d       t        |g t        |            }|d   D cg c]  }|d   dk(  s| }}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c c}w )uH   보안 키워드 없는 일반 task에서는 medium 리스크 미생성.r!   u$   # 로깅 개선
로그 포맷 변경r	   r%   r'   r   rh   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sr   rS  rI  rJ  rK  NrT  rU  s              r   $test_no_false_positive_on_clean_taskz=TestMaatFallbackEnhanced.test_no_false_positive_on_clean_task  s    9,-	Y""#JK%iS]C#)'?Paa
mx6OPP< %A% A%%%% A%%%%%%s%%%s%%%%%%<%%%<%%% %%%A%%%%%%% Qs   E+E+N)rT   rU   rV   rW   rQ  rV  rY  rX   r   r   rA  rA    s    0$&&r   rA  c                       e Zd ZdZd Zd Zy)TestApiKeyValidationu'   OPENAI_API_KEY 사전 검증 테스트.c                 B   t        |dz        }t        |      j                  d       t        j                  t
        j                  i d      5  t        |g t        |            }ddd       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}||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}}|d   }d}||u}|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y# 1 sw Y   xY w)uW   OPENAI_API_KEY 없어도 companion 호출을 시도하고, 실패 시 마아트 폴백.r!   r`   T)clearr/   Nr   r   rh   r   r7   rP   r;   fallback_reasonr   r   rH   r   ro   rp   )is not)z%(py1)s is not %(py4)s)r=   r   r>   r   dictr  environr   r?   r@   rA   rC   rD   rr   rs   rt   )rF   rG   r0   rH   rI   rJ   rK   rL   rM   rv   rw   s              r   6test_no_api_key_still_attempts_companion_then_fallbackzKTestApiKeyValidation.test_no_api_key_still_attempts_companion_then_fallback  sf   9,-	Y"":.ZZ

Bd3 	%#!"8}F	 h2?2?2222?222222?2222222 * F**** F*** ******F***F*******'(44(4444(444(4444444444	 	s   HH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(  }|slt        j                  d|fd||f      t        j                  |      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# 1 sw Y   xY w)uD   OPENAI_API_KEY가 있으면 companion 호출을 시도해야 한다.r!   r`   OPENAI_API_KEYzsk-test-keyr*   r+   r7  Tr.   r/   Nr   r   rh   r   r7   rP   r;   r=   r   r>   r   r   r`  r  ra  r   r?   r@   rA   rC   rD   rR   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4444#4444444#44444444  	 	s<   D6(D*6DD*D6D'#D**D3	/D66D?N)rT   rU   rV   rW   rb  rf  rX   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 }|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}}d}d   }	||	v }|slt        j                  d|fd||	f      t        j                  |      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   DxY w# 1 sw Y   IxY w)uE   _run_codex_companion이 subprocess.run에 env를 전달해야 한다.r!   r`   c                      j                  |       t               }d|_        t        j                  g g d      |_        d|_        |S )Nr   r   r   )updater   r   r   r   r   r   )r}   r   mockcaptured_kwargss      r   capture_runzJTestSubprocessEnvPassing.test_subprocess_receives_env.<locals>.capture_run  s@    ""6*;DDO**r"%EFDKDKKr   rd  sk-testr*   r   r7  Tr+   r   )_run_codex_companionztest promptr.   Nenvr   r   rm  r   ro   rp   z%(py1)s in %(py4)sr7   rP   r;   )r=   r   r>   r   r`  r  ra  r   rp  r?   r@   rA   rr   rs   rt   rC   rD   )rF   rG   r0   rn  rp  rI   rK   rv   rw   rJ   rL   rM   rm  s               @r   test_subprocess_receives_envz5TestSubprocessEnvPassing.test_subprocess_receives_env  sg   9,-	Y"":.	 ZZ

%5y$AB 	O'[A O<4P OE(8MNOO	O 'u''''u'''u''''''''''''''''9?5#99#99999#9999999#99999999O OO O	O 	Os<   G%G3G GG G
GG	GG$N)rT   rU   rV   rW   rs  rX   r   r   rh  rh  	  s
    9:r   rh  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(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}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}}g }d}|d   }|j$                  } |       }||v }|}|sd}|d   }||v }|}|s&t        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }|j'                  |       |s_t        j                  dfd f      t        j                  |      t        j                  |      d!z  }d"d#|iz  }|j'                  |       t        j(                  |d$      i z  }d%d&|iz  }t        t        j                  |            dx}x}x}x}x}x}x}x}x}}y# 1 sw Y   xY w# 1 sw Y   xY w)'uI   companion 타임아웃 시 fallback_reason에 타임아웃 사유 포함.r!   r`   rd  ro  r*   nodex   r   r   r/   Nr   r   rh   r   r7   rP   r;   r^  r   r   rH   r   ro   rp   r   u   타임아웃)zF%(py3)s in %(py10)s
{%(py10)s = %(py8)s
{%(py8)s = %(py6)s.lower
}()
})rn   r;   rK  py10z%(py12)spy12)z%(py15)s in %(py18)s)py15py18z%(py20)spy20rb   zassert %(py23)spy23)r=   r   r>   r   r`  r  ra  r   r   r   r?   r@   rA   rC   rD   rr   rs   rt   lowerr  _format_boolop)rF   rG   r0   rH   rI   rJ   rK   rL   rM   rv   rw   ru   r   @py_assert7@py_assert9r   @py_assert14@py_assert17@py_assert16@py_format11@py_format13@py_format19@py_format21@py_format22@py_format24s                            r   &test_companion_timeout_includes_reasonz9TestFallbackReason.test_companion_timeout_includes_reason+  s0   9,-	Y"":.ZZ

%5y$AB 	'Z5N5NSYcf5gh )'#%#&x=	 h2?2?2222?222222?2222222 * F**** F*** ******F***F*******lylF#45l5;;l;=ly==llSYZkSllSlAllllly=lllylll5lll;lll=lllllllSlllllllSllllllllllllllll 	 	s$   #L;2L.
L;.L8	3L;;M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(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                   |	            dx}x}}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   8xY w# 1 sw Y   =xY w)uG   companion 비정상 종료 시 fallback_reason에 에러 사유 포함.r!   r`   rb   r   r   rd  ro  r*   r+   r/   Nr   r   rh   r   r7   rP   r;   r^  r   r   rH   r   ro   rp   )r=   r   r>   r   r   r   r   r   r`  r  ra  r   r?   r@   rA   rC   rD   rr   rs   rt   )rF   rG   r0   r   rH   rI   rJ   rK   rL   rM   rv   rw   s               r   $test_companion_error_includes_reasonz7TestFallbackReason.test_companion_error_includes_reason<  sV   9,-	Y"":.[
 !

5
ZZ

%5y$AB 	'jA )'#%#&x=	 h2?2?2222?222222?2222222 * F**** F*** ******F***F******* 	 	s$   .G<G
G
G	GG!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}||u }|slt        j                  d|fd||f      t        j                  |      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# 1 sw Y   xY w)u)   Codex 성공 시 fallback_reason은 None.r!   r`   rd  ro  r*   r+   r7  Tr.   r/   Nr^  r4   r6   r7   rP   r;   re  rR   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	 '(0D0(D0000(D000(000D0000000  	 	s<   D5(D)6DD)D5D&"D))D2	.D55D>N)rT   rU   rV   rW   r  r  r  rX   r   r   ru  ru  (  s    &m"+*1r   ru  c                   "    e Zd ZdZd Zd Zd Zy)TestSanitizeGateIntegrationu4   Codex 호출 전 PII 마스킹 자동 적용 검증.c           	         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 }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d
x}x}}r|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  }t        j                  d      dz   d|iz  }t        t        j                  |            d
x}}d}||
v}|st        j                  d|fd||
f      t        j                  |      dt        j                         v st        j                  |
      rt        j                  |
      nddz  }t        j                  d      dz   d|iz  }t        t        j                  |            d
x}}y
y
# 1 sw Y   x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   r}   r  rb   re   r  r  r   r   )r   r   r  r   r  r  r  r  r  r  r   r   r   r   r   r   )	r}   r   r   r  r  r  r  rl  captured_promptss	           r   rn  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*   r   r7  Tr+   r/   Nr3   r4   r6   r7   rP   r;   r   z010-1234-5678)not in)z%(py1)s not in %(py3)sprompt_textr   u)   전화번호가 마스킹되지 않았음r   rp   z900101-1234567u)   주민번호가 마스킹되지 않았음)r=   r   r>   r   r   r?   r@   rA   rC   rD   rr   rs   rt   rB   )rF   rG   r0   rn  rH   rI   rJ   rK   rL   rM   r  rv   rw   r  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"b?+5bbb?+bbb?bbbbbb+bbb+bbbb7bbbbbbb#c#;6ccc#;ccc#cccccc;ccc;cccc8ccccccc  	 	s#   J I3$J 3I=	8J  J
c                 T   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 }|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# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   $xY w)uD   sanitize_gate import 실패해도 codex_gate_check는 정상 동작.r!   u!   # 설계
연락처: 010-1234-5678z$codex_gate_check._SANITIZE_AVAILABLEFr*   r+   r7  Tr.   r/   Nr3   r4   r6   r7   rP   r;   r   r   rh   r   rQ   rR   s
             r   %test_sanitize_unavailable_still_workszATestSanitizeGateIntegration.test_sanitize_unavailable_still_works  sG   9,-	Y""#GH&r*	95A 	'i@ <4P -"+')'<F	 f~%%~%%%%~%%%~%%%%%%%%%%h4#44#44444#4444444#44444444  	 	s<    FFF+F3FFFF	FF'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 }|slt        j                  d|fd||f      t        j                  |      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)u7   PII 없는 프롬프트에서는 마스킹 감지 0건.r!   u+   # 일반 설계 문서
로깅 개선 작업r*   r+   r7  Tr.   r/   Nr3   r4   r6   r7   rP   r;   rQ   rR   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$   C:C.C:.C7	3C::DN)rT   rU   rV   rW   r  r  r  rX   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           	      r   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                  } |       }|st        j                  d      dz   dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }	t        t        j                  |	            dx}}t!        j"                  |j%                               }
|
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}||
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}}|
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# 1 sw Y   xY w# 1 sw Y   xY w) u9   Codex 성공 시 결과 파일이 생성되어야 한다.r!   r`   memoryeventsTparentsr*   r+   r7  ztask-test-001r0   r1   r2   task_idNztask-test-001.codex-gateu$   결과 파일이 생성되어야 함C
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}	gate_filer!  r  rh   r   r7   rP   r;   	timestampr   r   datar   ro   rp   r3   )r=   r   r>   r"  r   r   r   existsr?   rB   rr   rs   rt   rA   rC   rD   r   loads	read_textr@   )rF   rG   r0   
events_dirr   rH   r  ru   rJ   rL   r  rI   rK   rM   rv   rw   s                   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I#IIIIIIIyIIIyIIIIII!IIIIIIzz)--/0I1/1/1111/111111/1111111"{d""""{d"""{""""""d"""d"""""""F|-vf~-|~----|~---|---~------- 	 	s$   L,)LL,L)	$L,,L6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                  } |       }|st        j                  d      dz   dt        j                         v st        j                  |      rt        j                   |      ndt        j                   |      t        j                   |      dz  }t#        t        j$                  |            dx}}t'        j(                  |j+                               }	|	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# 1 sw Y   oxY w)uC   마아트 폴백 시에도 결과 파일이 생성되어야 한다.r!   r`   r  r  Tr  rb   r   r   r*   r+   ztask-test-002r  Nztask-test-002.codex-gateu5   폴백 시에도 결과 파일이 생성되어야 함r  r  r!  r   r   rh   r   r7   rP   r;   )r=   r   r>   r"  r   r   r   r   r   r   r  r?   rB   rr   rs   rt   rA   rC   rD   r   r  r  r@   )rF   rG   r0   r  r   r  ru   rJ   rL   r  rI   rK   rM   s                r   'test_gate_file_created_on_maat_fallbackzBTestGateFileAutoGeneration.test_gate_file_created_on_maat_fallback  sv   9,-	Y"":.(83
&[
 !

#
#*= 	#!"8}'		 !;;	Z!Z!ZZ#ZZZZZZZyZZZyZZZZZZ!ZZZZZZzz)--/0H~00~0000~000~0000000000	 	s   /G55G?c                    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t        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  }	t        j                   d      dz   d|	iz  }
t#        t        j$                  |
            d
x}x}}y
# 1 sw Y   LxY w)uA   task_id 없으면 결과 파일이 생성되지 않아야 한다.r!   r`   r  r  Tr  r*   r+   r/   Nz*.codex-gater   rh   rX  r   
gate_filesrI  u)   task_id 없으면 결과 파일 미생성z
>assert %(py8)srK  )r=   r   r>   r"  r   r   r   r   globr   r?   r@   rr   rs   rt   rA   rB   rC   rD   )rF   rG   r0   r  r   r  rK   r   r   rM   rP  s              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PP!PPPPPPsPPPsPPPPPP:PPP:PPPPPP!PPP%PPPPPPPP	 	s   F==GN)rT   rU   rV   rW   r  r  r  rX   r   r   r  r    s    n.012Qr   r  c                   (    e Zd ZdZd Zd Zd Zd Zy)TestNormalizeAffectedItemu8   _normalize_affected_item 헬퍼 함수 단위 테스트.c                    t        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}}d	}||u }|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*   문자열 입력 시 (path, False) 반환.file.pyrh   rj   r  rl   ro   rp   NFr4   z%(py0)s is %(py3)sis_new	r   r?   r@   rr   rs   rt   rA   rC   rD   rF   r  r  rK   ru   rv   rw   s          r   "test_string_returns_path_and_falsez<TestNormalizeAffectedItem.test_string_returns_path_and_false  s    /	:f  ty    ty      t   t   y       vvvvr   c                    t        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}}d}||u }|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
)u2   dict + is_new=True 입력 시 (path, True) 반환.znew_file.pyTr  r  rh   rj   r  rl   ro   rp   Nr4   r  r  r  r  s          r   test_dict_with_is_new_truez4TestNormalizeAffectedItem.test_dict_with_is_new_true  s    /RV0WXf$$t}$$$$t}$$$$$$t$$$t$$$}$$$$$$$v~vvvr   c                    t        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}}d}||u }|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
)u4   dict + is_new=False 입력 시 (path, False) 반환.zexisting.pyFr  rh   rj   r  rl   ro   rp   Nr4   r  r  r  r  s          r   test_dict_with_is_new_falsez5TestNormalizeAffectedItem.test_dict_with_is_new_false  s    /RW0XYf$$t}$$$$t}$$$$$$t$$$t$$$}$$$$$$$vvvvr   c                    t        ddi      \  }}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}}d	}||u }|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에 is_new 키 없으면 False 기본값.r  r  rh   rj   rl   ro   rp   NFr4   r  r  r  r  s          r   'test_dict_without_is_new_defaults_falsezATestNormalizeAffectedItem.test_dict_without_is_new_defaults_false  s    /0CDf  ty    ty      t   t   y       vvvvr   N)rT   rU   rV   rW   r  r  r  r  rX   r   r   r  r    s    Br   r  c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestIsNewMaatFallbackuC   is_new 메타데이터에 따른 마아트 폴백 동작 테스트.c                    t        |dz        }t        |      j                  d       t        |dddgt        |            }|d   }d}||u }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      d	z  }d
d|iz  }t        t	        j                  |            dx}x}}|d   D 	cg c]  }	|	d   dk(  s|	 }
}	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}}d}|
d   d   }||v }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      d	z  }d
d|iz  }t        t	        j                  |            dx}x}}yc c}	w )u1   is_new=True인 미존재 파일은 info severity.r!   r`   zbrand_new.pyTr  r3   r4   r6   r7   rP   r;   Nr	   r%   inforb   rh   rX  r   
info_risksrI  rJ  rK  u   신규 파일r   r&   r   rr  r=   r   r>   r   r?   r@   rA   rC   rD   r   rr   rs   rt   )rF   rG   r0   rH   rI   rJ   rK   rL   rM   re   r  r   r   rP  s                 r   test_new_file_missing_is_infoz3TestIsNewMaatFallback.test_new_file_missing_is_info#  s   9,-	Y"":.%$56M

 f~%%~%%%%~%%%~%%%%%%%%%%!'LAAjMV4KaL
L:#!#!####!######s###s######:###:######!#######>*Q-">>">>>>>">>>>>>>">>>>>>>> M   	I0I0c                    t        |dz        }t        |      j                  d       t        |dddgt        |            }|d   }d}||u }|slt	        j
                  d|fd	||f      t	        j                  |      t	        j                  |      d
z  }dd|iz  }t        t	        j                  |            dx}x}}|d   D 	cg c]  }	|	d   dk(  s|	 }
}	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}}d}|
d   d   }||v }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      d
z  }dd|iz  }t        t	        j                  |            dx}x}}yc c}	w )u2   is_new=False인 미존재 파일은 high severity.r!   r`   zshould_exist.pyFr  r3   Tr4   r6   r7   rP   r;   Nr	   r%   r#   rb   rh   rX  r   rH  rI  rJ  rK  u   오타 또는 삭제됨r   r&   r   rr  r  rF   rG   r0   rH   rI   rJ   rK   rL   rM   re   rH  r   r   rP  s                 r   "test_existing_file_missing_is_highz8TestIsNewMaatFallback.test_existing_file_missing_is_high2  s   9,-	Y"":.%'59:M

 f~%%~%%%%~%%%~%%%%%%%%%%!'LAAjMV4KaL
L:#!#!####!######s###s######:###:######!#######(HJqM-,HH(,HHHHH(,HHHH(HHH,HHHHHHHH Mr  c                    t        |dz        }t        |      j                  d       t        |dgt        |            }|d   }d}||u }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }d	d
|iz  }t        t	        j                  |            dx}x}}|d   D 	cg c]  }	|	d   dk(  s|	 }
}	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c c}	w )uE   문자열 형식(기존 호환)의 미존재 파일은 high severity.r!   r`   zmissing_file.pyr3   Tr4   r6   r7   rP   r;   Nr	   r%   r#   rb   rh   rX  r   rH  rI  rJ  rK  r  r  s                 r    test_string_missing_file_is_highz6TestIsNewMaatFallback.test_string_missing_file_is_highA  sA   9,-	Y"":.%M

 f~%%~%%%%~%%%~%%%%%%%%%%!'LAAjMV4KaL
L:#!#!####!######s###s######:###:######!####### Ms   G+G+c           	         t        |dz        }t        |      j                  d       |dz  }|j                  d       t        |t        |      ddddd	d
dgt        |            }|d   }d}||u }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            dx}x}}|d   D 
cg c]  }
|
d   dk(  s|
 }}
|d   D 
cg c]  }
|
d   dk(  s|
 }}
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}}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c c}
w c c}
w ) u&   문자열 + dict 혼합 형식 지원.r!   r`   z	exists.pyz# okznot_here.pyznew_module.pyTr  z
deleted.pyFr3   r4   r6   r7   rP   r;   Nr	   r%   r#   r  rg   rh   rX  r   rH  rI  rJ  rK  rb   r  r  )rF   rG   r0   existingrH   rI   rJ   rK   rL   rM   re   rH  r  r   r   rP  s                   r    test_mixed_format_affected_filesz6TestIsNewMaatFallback.test_mixed_format_affected_filesO  s%   9,-	Y"":.k)F#%H(D9%7	 M	
 f~%%~%%%%~%%%~%%%%%%%%%%!'LAAjMV4KaL
L!'LAAjMV4KaL
L:#!#!####!######s###s######:###:######!#######:#!#!####!######s###s######:###:######!####### MLs   .L><L>	MMc                 >   t        |dz        }t        |      j                  d       |dz  }|j                  d       t        |t        |      ddgt        |            }|d   D cg c]  }d|j	                  dd	      v s| }}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c c}w )uD   실제 존재하는 파일은 is_new 여부 무관 리스크 없음.r!   r`   zreal.pyz# real fileTr  r	   r&   r   r   rh   rX  r   
file_risksrI  rJ  rK  N)r=   r   r>   r   r   r   r?   r@   rr   rs   rt   rA   rC   rD   )rF   rG   r0   r  rH   re   r  rK   r   r   rM   rP  s               r   test_existing_file_no_riskz0TestIsNewMaatFallback.test_existing_file_no_riskg  s   9,-	Y"":.i'M*%(mt45M
 "(ZAI}VXAY4YaZ
Z:#!#!####!######s###s######:###:######!####### [s   )FFN)	rT   rU   rV   rW   r  r  r  r  r  rX   r   r   r  r     s    M?I$$0$r   r  r   )-rW   builtinsrr   _pytest.assertion.rewrite	assertionrewriter?   r   r  r   syspathlibr   unittest.mockr   r   r  insertr=   __file__parentr   r   r   r   r   r   r   rZ   rz   r   r   r   r   r(  r*  r5  rA  r[  rh  ru  r  r  r  r  rX   r   r   <module>r     sZ     	  
  * 3tH~,,334 5 ] ]
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X 8U$ U$r   