
    iL+                    
   d Z ddlmZ ddlZddlmc mZ ddl	Z
ddlZddlZddlmZ ddlZ ee      j#                         j$                  d   ZddZ edd      Zdd	Zd
 Zd Zd Zd Zd Zd Zd Zd Zd Zd Z d Z!d Z"y)u  tests/regression/test_silent_corruption.py — task-2471 회귀 테스트.

토르가 commit ea702b51 에서 추가한 ``utils.silent_corruption_guard`` 의
3 fail-closed check (mergedAt / mergeCommit.oid / origin/main ancestry) 와
``verify_done_preconditions`` 를 영구 차단한다.

외부 호출 (subprocess, gh, git) 은 monkeypatch 로 fake 처리.
실제 ``gh`` / ``git`` 호출 절대 금지 — 격리된 단위 테스트.

헤임달(개발2팀 테스터) 작성.
    )annotationsN)Path   c                <   t         |z  }t        j                  j                  | t	        |            }||j
                  t        d|       t        j                  j                  |      }|t        j                  | <   |j
                  j                  |       |S )uO   Worktree 의 절대 경로로 모듈을 직접 로드 (sys.path 충돌 회피).zcannot load spec for )	WORKSPACE	importlibutilspec_from_file_locationstrloaderImportErrormodule_from_specsysmodulesexec_module)mod_namefile_rel	file_pathspecmodules        >/home/jay/workspace/tests/regression/test_silent_corruption.py_load_moduler      s    H$I>>11(C	NKD|t{{*1)=>>^^,,T2F"CKKKKF#M    "silent_corruption_guard_test_aliasz utils/silent_corruption_guard.pyc                    g dddfd
}| j                  t        d|       | j                  t        j                  dd        S )u/  ``silent_corruption_guard._run`` 을 사전 정의된 응답 맵으로 fake.

    ``response_map`` 은 ``cmd[0:N]`` 의 prefix tuple 또는 임의 매칭 함수를
    키로 받아 ``(rc, stdout, stderr)`` 를 반환.

    실제 호출 사실을 추적하기 위해 호출 로그 list 를 반환.
    N)cwdtimeoutc               B   j                  t        |       ||d       | r| d   dk(  rj                  dd      S | rb| d   dk(  rZt        |       dkD  r| d   nd}|dk(  rj                  d	d
      S |dk(  rj                  dd      S |dk(  rj                  dd
      S y
)N)cmdr   r   r   gh)r   z{} git   r!   fetch	git_fetchr   r!   r!   	rev-parsegit_rev_parse)r   zabc123
r!   z
merge-basegit_merge_base)appendlistgetlen)r   r   r   subcallsresponse_maps       r   	_fake_runz_patch_run.<locals>._fake_run:   s    T#YswGH3q6T>##D-883q6U?HqL#a&bCg~#''[AAk!#''9LMMl"#''(8+FFr   _runsleepc                     y N _s    r   <lambda>z_patch_run.<locals>.<lambda>K       r   )setattrscgtime)monkeypatchr0   r1   r/   s    ` @r   
_patch_runr?   0   sC     E"D  VY/'>:Lr   c                   t        j                  d ddid      }t        | dd|dfi       t        j	                  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   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}g }d}|d   }	||	v }
|
}|
sd}|d   d   d   d   }||v }|}|st        j                  d|
fd||	f      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}}y )&NoiddeadbeefmergedAtmergeCommitr    r   r!   *   
owner/repomain	pr_numberrepobase_branchokFisz%(py1)s is %(py4)spy1py4assert %(py6)spy6detailfailed_check	merged_at==z%(py1)s == %(py4)srD   reasonchecksinz%(py3)s in %(py6)spy3rU   %(py8)spy8z%(py11)s in %(py14)spy11py14%(py16)spy16r#   assert %(py19)spy19)jsondumpsr?   r<   verify_done_preconditions
@pytest_ar_call_reprcompare	_safereprAssertionError_format_explanationr*   _format_boolop)r>   payloadresult@py_assert0@py_assert3@py_assert2@py_format5@py_format7@py_assert1@py_assert5@py_assert4@py_assert10@py_assert13@py_assert12@py_format9@py_format15@py_format17@py_format18@py_format20s                      r   $test_mergedAt_null_fails_verify_doner   T   s7   jjdE:;NOPG{TAw#345**<V + F $< 5 <5    <5   <   5       (N+:{:+{::::+{:::+:::{:::::::: ) :)) Z 6(;KH;U<< Z < .   :)  i   i *   		 Z <  i .8  i<   		       r   c                   t        j                  dddid      }t        | dd|dfi       t        j	                  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}}g }d}|d   }	|	j                  }
 |
       }||v }|}|s d}|d   }|j                  } |       }||v }|}|sNt        j                  d|fd||f      t        j                  |      t        j                  |	      t        j                  |
      t        j                  |      dz  }dd|iz  }|j                  |       |st        j                  dfdf      t        j                  |      t        j                        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}x}x}}y) u@   check_pr_merged_at 단독 호출 시 mergedAt=null → ok=False.NrA   xrC   r    r   r!      rG   rM   FrN   rP   rQ   rT   rU   nullr\   z
not mergedr^   zF%(py3)s in %(py10)s
{%(py10)s = %(py8)s
{%(py8)s = %(py6)s.lower
}()
}rb   rU   rd   py10%(py12)spy12zJ%(py15)s in %(py22)s
{%(py22)s = %(py20)s
{%(py20)s = %(py18)s.lower
}()
}py15py18py20py22%(py24)spy24r#   assert %(py27)spy27)rm   rn   r?   r<   check_pr_merged_atrp   rq   rr   rs   rt   lowerr*   ru   )r>   rv   rw   rx   ry   rz   r{   r|   r}   r~   @py_assert7@py_assert9r   @py_assert14@py_assert17@py_assert19@py_assert21@py_assert16@py_format11@py_format13@py_format23@py_format25@py_format26@py_format28s                           r   -test_check_pr_merged_at_null_returns_ok_falser   c   s   jjdE3<HIG{TAw#345##A|4F$< 5 <5    <5   <   5       Y6YVH%Y%++Y+-Y6--YYAQYAQAWAWYAWAYYAY1YYYYY6-YYY6YYY%YYY+YYY-YYYYYYYAYYYYYYYAQYYYAWYYYAYYYYYYYYYYYYYYYYr   c                   t        j                  dd d      }t        | dd|dfi       t        j	                  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   }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 )N2026-05-07T00:00:00ZrC   r    r   r!   c   rG   rH   rI   rM   FrN   rP   rQ   rT   rU   rV   rW   merge_commit_oidrY   r[   
rm   rn   r?   r<   ro   rp   rq   rr   rs   rt   r>   rv   rw   rx   ry   rz   r{   r|   s           r   ,test_merge_commit_oid_null_fails_verify_doner   r   s    jj&<TRSG{TAw#345**<V + F $< 5 <5    <5   <   5       (N+A/AA+/AAAAA+/AAAA+AAA/AAAAAAAAr   c                   t        j                  di d      }t        | dd|dfi       t        j	                  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   }|j                  } |       }	||	v }|st        j                  d|fd||	f      t        j                  |      t        j                  |      t        j                  |      t        j                  |	      dz  }
dd|
iz  }t        t        j                  |            dx}x}x}x}}	y)u9   mergeCommit 객체는 있지만 oid 키가 없는 경우.r   rC   r    r   r!      rG   rM   FrN   rP   rQ   rT   rU   NrA   r\   r^   )zD%(py1)s in %(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py4)s.lower
}()
})rR   rS   rU   rd   zassert %(py10)sr   )rm   rn   r?   r<   check_pr_merge_commit_oidrp   rq   rr   rs   rt   r   )r>   rv   rw   rx   ry   rz   r{   r|   r~   r   r   r   s               r   #test_merge_commit_missing_oid_fieldr   ~   s   jj&<RPQG{TAw#345**2|<F$< 5 <5    <5   <   5       ,F8$,$**,*,,5,,,,,5,,,,5,,,$,,,*,,,,,,,,,,,,r   c                   t        j                  dddid      }t        | d|dfddd	d
       t        j	                  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   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)u:   merge-base --is-ancestor 가 rc!=0 인 경우 fail-closed.r   rA   merge_sha_xyzrC   r   r!   r&   )r   zorigin_sha_123
r!   )r#   r!   znot ancestorr    r%   r(   r)   7   rG   rH   rI   rM   FrN   rP   rQ   rT   rU   NrV   rW   ancestryrY   r[   r   r   s           r   +test_ancestry_check_fails_when_not_ancestorr      s
   jj*/ G gr"$85		
 **<V + F $< 5 <5    <5   <   5       (N+9z9+z9999+z999+999z9999999r   c                "   t        g d      fd}| j                  t        d|       | j                  t        j                  dd        t        j	                  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}}g }d}|d   }	|	j                  }
 |
       }||v }|}|s d}|d   }|j                  } |       }||v }|}|sNt        j                  d|fd||f      t        j                  |      t        j                  |	      t        j                  |
      t        j                  |      dz  }dd|iz  }|j                  |       |st        j                  dfdf      t        j                  |      t        j                        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}x}x}}y) uL   origin/<base> SHA 가 2회 fetch 사이 흔들리면 race detected → fail.)zsha_a
zsha_b
zsha_c
zsha_d
c                h    ~| d   dk(  r| d   dk(  rdt        d      dfS | d   dk(  r	| d   dk(  ryy)	Nr   r"   r#   r'   zsha_x
r!   r$   r&   )next)r   _kwargssha_iters     r   r1   z:test_ancestry_unstable_origin_sha_fails.<locals>._fake_run   sL    q6U?s1v4tHi0"55q6U?s1v0r   r2   r3   c                     y r5   r6   r7   s    r   r9   z9test_ancestry_unstable_origin_sha_fails.<locals>.<lambda>   r:   r   	merge_sharH   rL   rM   FrN   rP   rQ   rT   rU   Nracer\   unstabler^   r   r   r   r   r   r   r   r   r#   r   r   )iterr;   r<   r=   check_origin_main_ancestryrp   rq   rr   rs   rt   r   r*   ru   )r>   r1   rw   rx   ry   rz   r{   r|   r}   r~   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   s                           @r   'test_ancestry_unstable_origin_sha_failsr      s      H
 VY/'>:++KV+LF$< 5 <5    <5   <   5       W6WVH%W%++W+-W6--WWvh?OW?O?U?UW?U?WW?W1WWWWW6-WWW6WWW%WWW+WWW-WWWWWWW?WWWWWWW?OWWW?UWWW?WWWWWWWWWWWWWWWWr   c                   d}t        j                  dd|id      }t        | d|dfdd| ddfdd	       t        j	                  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(  }|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   }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   }||v }|slt        j                  d |fd!||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}|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   }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   }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 )$N(1234567890abcdef1234567890abcdef12345678r   rA   rC   r   r!   r&   
r      rG   rH   rI   rM   TrN   rP   rQ   rT   rU   rV   merge_commit_sharY   )z%(py1)s == %(py3)sr   )rR   rb   zassert %(py5)spy5rX   r[   r]   r^   )z%(py1)s in %(py4)sr   r   )rm   rn   r?   r<   ro   rp   rq   rr   rs   rt   @py_builtinslocals_should_repr_global_name)r>   r   rv   rw   rx   ry   rz   r{   r|   @py_format4@py_format6r]   s               r   #test_all_checks_pass_yields_ok_truer      s   :Ijj*y) G gr"$I;b!126)		
 **Lf + F $<4<4<4<4(./</9<<<</9<<</<<<<<<9<<<9<<<<<<<(K(B,BB(,BBBBB(,BBBB(BBB,BBBBBBBB'vh''8'''''8''''8'''''''''''Hh'F+t$,,$,,,,$,,,$,,,,,,,,,,$%d+3t3+t3333+t333+333t3333333*d#+t+#t++++#t+++#+++t+++++++r   c                   t        | ddi       t        j                  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}}g }d}|d   }||v }	|	}|	sd}
|d   }|
|v }|}|st        j                  d|	fd||f      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}}y)uL   gh rc != 0 → mergedAt check 실패 → verify_done_preconditions ok=False.r    )r#   r!   zgh: not authenticatedr#   rG   rH   rI   rM   FrN   rP   rQ   rT   rU   Nr\   rX   r^   r`   ra   rc   rd   re   rf   ri   rj   rk   rl   )
r?   r<   ro   rp   rq   rr   rs   rt   r*   ru   )r>   rw   rx   ry   rz   r{   r|   r}   r~   r   r   r   r   r   r   r   r   r   s                     r   $test_gh_command_failure_fails_closedr      s2   {T#CDE**,F + F $< 5 <5    <5   <   5       F4F6(#F4##F{FfX6FF{6F'FFFF4#FFF4FFF#FFFFFFF{6FFFF{FFF6FFFFFFFFFFFFFFFr   c                   d }| j                  t        d|       | j                  t        j                  dd        t        j                  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)uE   subprocess 예외 (FileNotFoundError 등) 시 fail-closed (ok=False).c                     ~ ~y)N)r!   zFileNotFoundError: gh not foundr6   )_cmdr   s     r   
_raise_runz=test_gh_subprocess_exception_fails_closed.<locals>._raise_run   s
    ':r   r2   r3   c                     y r5   r6   r7   s    r   r9   z;test_gh_subprocess_exception_fails_closed.<locals>.<lambda>   r:   r   r#   rG   rM   FrN   rP   rQ   rT   rU   N)	r;   r<   r=   r   rp   rq   rr   rs   rt   )r>   r   rw   rx   ry   rz   r{   r|   s           r   )test_gh_subprocess_exception_fails_closedr      s    ; VZ0'>:##A|4F$< 5 <5    <5   <   5       r   c                &   t        | ddi       t        j                  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}}g }d}|d   }||v }	|	}|	s d}
|d   }|j                  } |       }|
|v }|}|s&t        j                  d|	fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }|j                  |       |	st        j                  dfd
f      t        j
                  |
      t        j
                        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)u-   gh stdout 가 JSON 이 아니면 fail-closed.r    )r   znot-json garbager!   r   rG   rM   FrN   rP   rQ   rT   rU   NJSONr\   decoder^   r`   ra   rc   rd   )zJ%(py11)s in %(py18)s
{%(py18)s = %(py16)s
{%(py16)s = %(py14)s.lower
}()
})rg   rh   rj   r   %(py20)sr   r#   assert %(py23)spy23)r?   r<   r   rp   rq   rr   rs   rt   r   r*   ru   )r>   rw   rx   ry   rz   r{   r|   r}   r~   r   r   r   @py_assert15r   r   r   @py_format19@py_format21@py_format22@py_format24s                       r   )test_gh_returns_invalid_json_fails_closedr      sY   {T#>?@##A|4F$< 5 <5    <5   <   5       M6MVH%M6%%MMVH5EM5E5K5KM5K5MM5M)MMMMM6%MMM6MMM%MMMMMMM5MMMMMMM5EMMM5KMMM5MMMMMMMMMMMMMMMMr   c                   g fd}| j                  t        d|       t        j                  ddg d       s{t        j                  d      dz   d	d
t        j                         v st        j                        rt        j                        nd
iz  }t        t        j                  |            d   }|dd }g d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|dd        dz   d|iz  }t        t        j                  |            dx}x}}g }	d}||v }
|
}|
rd}||v }|}|sXt        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  }|	j                  |       |
rt        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  }|	j                  |       t        j                  |	d      i z  }d d!|iz  }t        t        j                  |            dx}x}	x}x}
x}}y)"uW   gh_cmd=["custom-gh"] 전달 시 실제 명령 prefix 가 그것으로 시작해야 함.c                t    ~j                  t        |              dt        j                  dddid      dfS )Nr   r   rA   yrC   r!   )r*   r+   rm   rn   )r   r   captureds     r   r1   z0test_gh_cmd_parameter_is_used.<locals>._fake_run  s5    S	"4::3s|LMrRRr   r2      rG   )z	custom-ghz--tokenT)gh_cmdu$   fake _run 이 호출되지 않았음z
>assert %(py0)spy0r   r   N   rY   r[   rQ   u   cmd prefix 미일치: z
>assert %(py6)srU   prviewr^   )z%(py3)s in %(py5)scmd0)rb   r   z%(py7)spy7)z%(py10)s in %(py12)s)r   r   z%(py14)srh   zassert %(py17)spy17)r;   r<   r   rp   _format_assertmsgr   r   r   rr   rs   rt   rq   r*   ru   )r>   r1   @py_format1r   rx   ry   rz   r{   r|   r}   r   r   @py_assert11r   @py_format8r   r   @py_format16r   r   s                      @r   test_gh_cmd_parameter_is_usedr     s   HS
 VY/1l3PQ;;;;;;;;;8;;;8;;;;;A;D8Y4Y844YYY84YYY8YYY4YYY8NtTVUVxj6YYYYYYYY*4*44<*F*FdN****44***4******4***4*******Fd***F******d***d**************r   c                    t         j                  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}}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)uS   check_origin_main_ancestry 가 빈 SHA 받으면 즉시 fail (subprocess 호출 X).r!   rH   r   rM   FrN   rP   rQ   rT   rU   Nemptyr\   r   r^   r   r   r   r   )z%(py15)s in %(py18)s)r   r   r   r   r#   r   r   )
r<   r   rp   rq   rr   rs   rt   r   r*   ru   )rw   rx   ry   rz   r{   r|   r}   r~   r   r   r   r   r   r   r   r   r   r   r   r   s                       r   *test_empty_merge_commit_sha_short_circuitsr  %  sP   ++BF+CF$< 5 <5    <5   <   5       X7XfX&X&,,X,.X7..X2DXxHXX2DHX2XXXXX7.XXX7XXX&XXX,XXX.XXXXXXX2DHXXXX2DXXXHXXXXXXXXXXXXXXXXr   )r   r   r   r   )r>   zpytest.MonkeyPatchr0   dictreturnr+   )#__doc__
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriterp   importlib.utilr   rm   r   pathlibr   pytest__file__resolveparentsr   r   r<   r?   r   r   r   r   r   r   r   r   r   r   r   r  r6   r   r   <module>r     s   
 #      
   N""$,,Q/		 (&HZ	B-:0X:,HG!N+0Yr   