
    gi;                     R   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
mZ ddlZ ed      Zedz  ZdZdeddfd	Zd$ded
ededefdZdedefdZ	 	 	 	 d%dededee   dz  dee   dz  dededz  deeef   fdZ	 	 d&dededededz  dee   dz  deeeef   fdZej3                          Zej6                  j9                  ed      Zededdfd       Zededdfd       Zededdfd       Z ededdfd        Z!ededdfd!       Z"ededdfd"       Z#ededdfd#       Z$y)'u<  
test_pre_push_guard.py — pre_push_guard.py 회귀 테스트 (Guard MVP Phase 1, task-2434)

시나리오 (7개):
    1. test_clean_tree_scope_match_qc_match          — clean + scope 일치 + qc 일치 → rc=0
    2. test_modified_outside_scope_fail_b1            — task scope 외 파일 modified → rc=1 (B-1)
    3. test_behind_origin_fail_b2                     — HEAD behind base ref → rc=1 (B-2)
    4. test_forbidden_path_match_fail_b3              — head_diff에 forbidden_paths 매치 → rc=1 (B-3)
    5. test_qc_warn_report_overall_pass_fail_b4       — qc=WARN + 보고서=OVERALL PASS → rc=1 (B-4)
    6. test_normal_push_pass                          — 정상 push 케이스 → rc=0
    7. test_system_ignore_excluded_b1                 — memory/logs/*.log modified → system-ignore로 PASS
    N)Pathz/home/jay/workspace/scriptszpre_push_guard.pyztask-2434-testpathreturnc                 Z     dt         dd f fd} |d        |ddd        |ddd	       y )
Nargsr   c                  f    t        j                  ddt              gt        |       z   dd       y )Ngit-CTcheckcapture_output)
subprocessrunstrlist)r   r   s    8/home/jay/workspace/tests/scripts/test_pre_push_guard.py_gz_init_git.<locals>._g#   s+    D#d)$tDz1t	
    initconfig
user.emailtest@test.com	user.nameTest)r   )r   r   s   ` r   	_init_gitr   "   s7    
# 
$ 

 vJx/xv&r   file_relcontentc           	         | |z  }|j                   j                  dd       |j                  |       t        j                  ddt        |       d|gdd       t        j                  ddt        |       ddd	| gdd       t        j                  ddt        |       d
dgddd      }|j                  j                         S )NTparentsexist_okr	   r
   addr   commitz-mzadd z	rev-parseHEADr   r   text)parentmkdir
write_textr   r   r   stdoutstrip)r   r   r   targetrs        r   _make_commitr.   -   s    H_F
MMt4
gNNE4TE8<d4NNE4THdd8*<MNd4	c$if54d	A 88>>r   c                     t        j                  ddt        |       dddgddd      }|j                  j	                         S )Nr	   r
   zrev-listz--max-parents=0r$   Tr%   )r   r   r   r*   r+   )r   r-   s     r   _get_first_shar0   <   s?    	c$i->G4d	A 88>>r   tmptask_idallowed_pathsforbidden_pathsqc_verdict_jsonreport_contentc                    | dz  }dD ]  }||z  j                  dd        ||xs ddg|xs g dd}|d	z  d
z  | dz  j                  t        j                  |      d       |d	z  dz  | dz  j                  d| dd       ||i d}	|d	z  dz  | dz  j                  t        j                  |	      d       ||d	z  dz  | dz  j                  |d       | dz  }
|
j                          t	        |
       ||
fS )u  
    tmp/workspace 구조 생성:
      memory/capabilities/<task_id>.json
      memory/tasks/<task_id>.md
      memory/events/<task_id>.qc-result
      memory/reports/<task_id>.md  (선택)
    또한 tmp/repo 에 최소 git repo 생성.
    반환: (workspace, repo)
    	workspace)zmemory/capabilitieszmemory/taskszmemory/eventszmemory/reportszmemory/logsTr   
scripts/**tests/**)pathsr4   )r2   allowed_resourcesmemorycapabilitiesz.jsonzutf-8)encodingtasksz.mdz# 
)r2   	qc_resultchecks_summaryeventsz
.qc-resultreportsrepo)r(   r)   jsondumpsr   )r1   r2   r3   r4   r5   r6   wsdcaprB   rF   s              r   _setup_workspacerL   D   sh    
{	B@ 4	atd34 "@|Z&@.4"
C (]^#	&77CC

3' D 
 (]W'#.::
WIR7 ; 
 $/UWXI(]X7): 66BB

9 C 
 !	h	"y_	4@@W 	A 	

 <DJJLdOt8Or   rI   rF   base_sha
extra_argsc           
         t         j                  t        t              d|dt        |       dt        |      g}|r|d|gz  }|r||z  }t	        j
                  |ddt        |             }|j                  |j                  |j                  fS )u:   pre_push_guard.py 실행 → (returncode, stdout, stderr).z	--task-idz--workspacez--cwdz
--base-shaT)r   r&   cwd)	sys
executabler   	_GUARD_PYr   r   
returncoder*   stderr)rI   rF   r2   rM   rN   cmdr-   s          r   
_run_guardrW   }   s    
 	IWs2wTC h''zs4dBHA<<188++r   z8pre_push_guard.py not yet implemented (Thor in progress))reasontmp_pathc                 8   d}t        | t        ddgd|      \  }}t        |d       t        |      }t        |d       t	        ||t        |      \  }}}d	}||k(  }	|	st        j                  d
|	fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }
t        j                  d| d| d|       dz   d|
iz  }t        t        j                  |            dx}	}y)u<   clean working tree + scope 일치 + qc=PASS → rc=0 (PASS).z7---
qc_verdict: PASS
---
# Report

## QC Verdict

PASS
r9   r:   PASSr3   r5   r6   scripts/hello.pytests/test_hello.pyrM   r   ==z%(py0)s == %(py3)srcpy0py3 expected PASS (rc=0) but got rc=
stdout=
stderr=
>assert %(py5)spy5NrL   TASK_IDr.   r0   rW   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgAssertionError_format_explanationrY   reportrI   rF   rM   rc   r*   rU   @py_assert2@py_assert1@py_format4@py_format6s               r   $test_clean_tree_scope_match_qc_matchr}      s   	"   '#Z0	HB )*d#H,- $BgIB 27  2                  +2$ixyQ    r   c                 R   t        | t        ddgd      \  }}t        |d       t        |      }t        |d       |dz  dz  }|j                  j                  d	d	
       |j                  d       t        j                  ddt        |      ddgd	d	       t        ||t        |      \  }}}d}||k(  }	|	st        j                  d|	fd||f      dt        j                         v st        j                  |      rt        j                   |      ndt        j                   |      dz  }
t        j"                  d| d| d|       dz   d|
iz  }t%        t        j&                  |            dx}	}||z   }g }	d}||v }|}|s8d}|j(                  } |       }||v }|}|sd}|j(                  } |       }||v }|}|sPt        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+                  |       |sjt        j                  d fd&f      t        j                   |      d"t        j                         v st        j                  |      rt        j                   |      nd"t        j                         t        j                   |      d'z  }d(d)|iz  }|	j+                  |       |st        j                  d fd*f      t        j                   |      d"t        j                         v st        j                  |      rt        j                   |      nd"t        j                         t        j                   |      d+z  }d,d-|iz  }|	j+                  |       t        j,                  |	d      i z  }t        j"                  d.|       d/z   d0|iz  }t%        t        j&                  |            dx}x}	x}x}x}x}x}x}x}x}x}}y)1uE   task scope 외 파일이 working tree에 modified → B-1 FAIL, rc=1.r9   r:   r[   r3   r5   r]   r^   	extensionzfoo.jsTr   z	modified
r	   r
   r"   zextension/foo.jsr   r_      r`   rb   rc   rd   z%expected FAIL (rc=1, B-1) but got rc=rh   ri   rj   rk   NzB-1workingdirtyinz%(py3)s in %(py5)scombinedrf   rk   %(py7)spy7zJ%(py10)s in %(py16)s
{%(py16)s = %(py14)s
{%(py14)s = %(py12)s.lower
}()
}py10py12py14py16%(py18)spy18zJ%(py21)s in %(py27)s
{%(py27)s = %(py25)s
{%(py25)s = %(py23)s.lower
}()
}py21py23py25py27%(py29)spy29z2Expected B-1 / dirty / working mention in output:

>assert %(py32)spy32)rL   rm   r.   r0   r'   r(   r)   r   r   r   rW   rn   ro   rp   rq   rr   rs   rt   ru   rv   lowerappend_format_boolop)rY   rI   rF   rM   out_of_scoperc   r*   rU   ry   rz   r{   r|   r   @py_assert4@py_assert0@py_assert9@py_assert13@py_assert15@py_assert11@py_assert20@py_assert24@py_assert26@py_assert22@py_format8@py_format17@py_format19@py_format28@py_format30@py_format31@py_format33s                                 r   #test_modified_outside_scope_fail_b1r      s     '#Z0HB )*d#H,- +%0LdT:L)NN	c$i(:;4 $BgIB 27  2                  0t9VHIfXV     H5 5H 	 X^^ ^-= 	-= =  HNN NL\ L\A\   5H                   	-=    !*      .6    .6    .<    .>     L\    BI      MU    MU    M[    M]        >hZH       r   c                 	   t        | t        dgd      \  }}| dz  }t        j                  ddddt	        |      gd	d	
       t        |d       t        |      }t        j                  ddt	        |      dddt	        |      gd	d	
       t        j                  ddt	        |      ddddgd	d	
       | dz  }t        j                  ddt	        |      t	        |      gd	d	
       t        j                  ddt	        |      dddgd	d	
       t        j                  ddt	        |      dddgd	d	
       t        |d       t        j                  ddt	        |      dddgd	d	
       t        ||t        |      \  }}}d}	||	k(  }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      dz  }t        j                  d | d!| d"|       d#z   d$|iz  }t        t        j                   |            d%x}
}	||z   }g }
d&}	|	|v }|}|s8d'}|j"                  } |       }||v }|}|sd(}|j"                  } |       }||v }|}|sPt        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%                  |       |sjt        j                  d)fd/f      t        j                  |      d+t        j                         v st        j                  |      rt        j                  |      nd+t        j                        t        j                  |      d0z  }d1d2|iz  }|
j%                  |       |st        j                  d)fd3f      t        j                  |      d+t        j                         v st        j                  |      rt        j                  |      nd+t        j                        t        j                  |      d4z  }d5d6|iz  }|
j%                  |       t        j&                  |
d      i z  }t        j                  d7|       d8z   d9|iz  }t        t        j                   |            d%x}x}
x}	x}x}x}x}x}x}x}x}}y%):u   base_sha 이후 HEAD가 뒤처진 상황 → B-2 FAIL, rc=1.

    실제 'behind origin' 상태를 흉내 내기 위해:
    origin 역할의 bare repo를 만들고 origin에 추가 커밋을 push한 후
    로컬을 fetch 없이 HEAD 고정.
    r9   r[   r   z
origin.gitr	   r   z--barez--initial-branch=mainTr   scripts/a.pyr
   remoter"   originpushz-uz	HEAD:maincloner   r   r   r   r   zscripts/origin_extra.pyr_   r   r`   rb   rc   rd   z%expected FAIL (rc=1, B-2) but got rc=rh   ri   rj   rk   NzB-2behindaheadr   r   r   r   r   r   r   r   r   r   r   r   r   r   z1Expected B-2 / behind / ahead mention in output:
r   r   )rL   rm   r   r   r   r.   r0   rW   rn   ro   rp   rq   rr   rs   rt   ru   rv   r   r   r   )rY   rI   rF   r   rM   r   rc   r*   rU   ry   rz   r{   r|   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   s                                  r   test_behind_origin_fail_b2r      s     '#nHB $FNN	"93v;G4
 ~&d#HNN	c$i5(CKH4 NN	c$ixE4 wENN	Vc%j14 NN	c%j(L/J4 NN	c%j(K@4 12NN	c%j&(K@4 $BgIB 27  2                  0t9VHIfXV     H5 5H  HNN N,< ,< <  8>> >K[ K[@[   5H                   ,<    !)      -5    -5    -;    -=     K[    AH      LT    LT    LZ    L\        =XJG       r   c                    t        | t        ddgddgd      \  }}t        |d       t        |      }t        |dd	       t	        ||t        |
      \  }}}d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }	t        j                  d| d| d|       dz   d|	iz  }
t        t        j                  |
            dx}}||z   }g }d}||v }|}|s8d}|j                  } |       }||v }|}|sd}|j                  } |       }||v }|}|sPt        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                  |       |sjt        j                  dfdf      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                        t        j                  |      d z  }d!d"|iz  }|j                  |       |st        j                  dfd#f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                        t        j                  |      d$z  }d%d&|iz  }|j                  |       t        j                   |d      i z  }t        j                  d'|       d(z   d)|iz  }t        t        j                  |            dx}x}x}x}x}x}x}x}x}x}x}}y)*uL   head_diff에 forbidden_paths 패턴 매칭 파일 포함 → B-3 FAIL, rc=1.r9   r:   zscripts/finish-task.shzscripts/auto_merge.pyr[   )r3   r4   r5   zscripts/safe.pyz#!/bin/bash
echo hi
)r   r_   r   r`   rb   rc   rd   z%expected FAIL (rc=1, B-3) but got rc=rh   ri   rj   rk   NzB-3	forbiddenscoper   r   r   r   r   r   r   r   r   r   r   r   r   r   z4Expected B-3 / forbidden / scope mention in output:
r   r   rL   rm   r.   r0   rW   rn   ro   rp   rq   rr   rs   rt   ru   rv   r   r   r   )rY   rI   rF   rM   rc   r*   rU   ry   rz   r{   r|   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   s                                r   !test_forbidden_path_match_fail_b3r   )  s     '#Z013JK	HB ()d#H/9QR#BgIB 27  2                  0t9VHIfXV     H5 5H  x~~ ~/? /? ? 7 hnn nN^ 7N^C^   5H                   /?    !,      08    08    0>    0@     7N^    DK      OW    OW    O]    O_        @zJ       r   c                    d}t        | t        dgd|      \  }}t        |d       t        |      }t        |d       t	        ||t        |      \  }}}d}||k(  }	|	st        j                  d	|	fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }
t        j                  d| d| d|       dz   d|
iz  }t        t        j                  |            dx}	}||z   }g }	d}||v }|}|s8d}|j                  } |       }||v }|}|sd}|j                  } |       }||v }|}|sPt        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                  |       |sjt        j                  dfdf      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                        t        j                  |      dz  }dd|iz  }|	j                  |       |st        j                  dfd f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                        t        j                  |      d!z  }d"d#|iz  }|	j                  |       t        j                   |	d      i z  }t        j                  d$|       d%z   d&|iz  }t        t        j                  |            dx}x}	x}x}x}x}x}x}x}x}x}}y)'u   qc-result=WARN + 보고서 본문에 'OVERALL PASS' → B-4 FAIL, rc=1.

    task-2431 사고 재발 방지 회귀 가드.
    z<---
title: Report
---
# Report

## QC Verdict

OVERALL PASS
r9   WARNr\   r   scripts/b.pyr_   r   r`   rb   rc   rd   z%expected FAIL (rc=1, B-4) but got rc=rh   ri   rj   rk   NzB-4qcwarnr   r   r   r   r   r   r   r   r   r   r   r   r   r   z,Expected B-4 / qc / warn mention in output:
r   r   r   )rY   rx   rI   rF   rM   rc   r*   rU   ry   rz   r{   r|   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   s                                 r   (test_qc_warn_report_overall_pass_fail_b4r   F  s   	*   '#n	HB ~&d#H~&#BgIB 27  2                  0t9VHIfXV     H5 5H   (8 (8 8 F hnn nFV FFV<V   5H                   (8    !%      )1    )1    )7    )9     FFV    =C      GO    GO    GU    GW        8zB       r   c                 :   d}t        | t        ddgg d|      \  }}t        |d       t        |      }t        |d       t	        ||t        |      \  }}}d	}||k(  }	|	st        j                  d
|	fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }
t        j                  d| d| d|       dz   d|
iz  }t        t        j                  |            dx}	}y)uO   정상 push 가능 케이스: clean tree + scope 내 커밋 + qc=PASS → rc=0.----
qc_verdict: PASS
---
## QC Verdict

PASS
r9   r:   r[   )r3   r4   r5   r6   zscripts/util.pyztests/test_util.pyr_   r   r`   rb   rc   rd   rg   rh   ri   rj   rk   Nrl   rw   s               r   test_normal_push_passr   m  s   	"   '#Z0HB ()d#H+, $BgIB 27  2                  +2$ixyQ    r   c                    d}t        | t        dgd|      \  }}t        |d       t        |      }t        |d       |dz  dz  }|j	                  d	d	
       |dz  j                  d       t        ||t        |      \  }}}d}	||	k(  }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      dz  }t        j                  d| d| d|       dz   d|iz  }t        t        j                  |            dx}
}	y)u   memory/logs/*.log 만 dirty → SYSTEM_IGNORE_PATTERN 적용으로 B-1 무시, rc=0.

    memory/logs/ 는 SYSTEM_IGNORE_PATTERN에 포함되므로 working tree dirty로
    처리하지 않아야 한다.
    r   r9   r[   r\   r   r   r=   logsTr   zfoo.logzlog content
r_   r   r`   rb   rc   rd   z3expected PASS (rc=0, system-ignore log) but got rc=rh   ri   rj   rk   N)rL   rm   r.   r0   r(   r)   rW   rn   ro   rp   rq   rr   rs   rt   ru   rv   )rY   rx   rI   rF   rM   log_dirrc   r*   rU   ry   rz   r{   r|   s                r   test_system_ignore_excluded_b1r     sI   	"   '#n	HB ~&d#H~& Xo&GMM$M.y$$_5 $BgIB 27  2                  >bT B6(	,    r   )zdata
)NNr[   N)NN)%__doc__builtinsrp   _pytest.assertion.rewrite	assertionrewritern   rG   r   rQ   pathlibr   pytest_SCRIPTS_DIRrS   rm   r   r   r.   r0   r   tuplerL   intrW   exists_GUARD_MISSINGmarkskipif_skip_if_missingr}   r   r   r   r   r   r    r   r   <module>r      sj      
  
 1211	'D 'T 't s S   #  8<9=,226	6$ 6 6$(I$46&*3i$&66 '*6 &)4Z	6 <At;L	6t '+.2,4 ,t ,c ,t,9t+,7<S#s]7K,. %%'';;%%E &   4 D  B $ 4  H < <$ < <F    8 t   L D T  > "T "d " "r   