
    j-                        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ZddlZddlmZmZ ddlmZ ddlZ ee      j'                         j(                  d   Zedz  dz  Zdefd	Zdefd
ZdedededdfdZdedefdZdedededededz  dedz  defdZdee   dededefdZ dededdfdZ! ejD                         d        Z#d Z$d Z%d Z&d Z'd Z(d  Z)d! Z*d" Z+d# Z,y)$u/  
test_stash_lifecycle_quarantine.py
task: task-2571 TODO-7 T-3

검증 목표:
- spec §2 unknown 분기 정책 검증
- unknown stash 는 항상 preserved
- wip stash 는 항상 preserved
- pre-task stash 는 dry-run 시 dry-run-pop
- skipped_unknown_count 검증

작성자: 하누만 (개발4팀 QA)
    N)datetimetimezone)Path   scriptszstash_audit.pyreturnc                     t        j                  d      } t               }t        j                  g d| d|       t        j                  g d| d|       t        j                  g d| d|       t        |       dz  j                  d	       t        j                  g d
| d|       t        j                  g d| d|       | S )u$   격리된 임시 git repo 초기화.zstash-lifecycle-test-)prefix)gitinit-qz-bmainTcwdcheckenv)r   configz
user.emailtest@example.com)r   r   z	user.nametesta.txthello)r   addr   )r   commitr   -mr   )tempfilemkdtemp_git_env
subprocessrunr   
write_text)dr   s     G/home/jay/workspace/tests/regression/test_stash_lifecycle_quarantine.py_init_temp_repor#   #   s     78A
*CNN6ATsSNNFAUY_bcNN9qRUV	!Ww""7+NN*3GNN8atQTUH    c                  j    t         j                  j                         } d| d<   d| d<   d| d<   d| d<   | S )u"   git 명령 실행용 환경변수.r   GIT_AUTHOR_NAMEr   GIT_AUTHOR_EMAILGIT_COMMITTER_NAMEGIT_COMMITTER_EMAIL)osenvironcopy)r   s    r"   r   r   0   sC    
**//
C#C0C &C!3CJr$   repo_dirmessagefilenamec                     t               }t        |       |z  }|j                  d| d       t        j                  dd|g| d|       t        j                  dddd	|g| d|       y
)u3   파일 하나 dirty 상태로 만들고 stash push.zdirty: 
r   r   Tr   stashpushr   Nr   r   r    r   r   )r-   r.   r/   r   fpaths        r"   _stash_pushr6   :   sf    
*CNX%E	wwir*+NNE5(+3ONNE7FD':PTZ]^r$   c                    t        j                  t        j                  t	        t
              dd| gdd      }|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d	z  }t        j                  d
|j                   d|j                         dz   d|iz  }t        t        j                   |            dx}x}}t#        j$                  |j&                        S )u7   stash_audit.py --json 실행 후 파싱된 dict 반환.z--jsonz--workspaceT)capture_outputtextr   ==)z2%(py2)s
{%(py2)s = %(py0)s.returncode
} == %(py5)sresult)py0py2py5u#   stash_audit.py 실행 실패 (exit=z
)
stderr: z
>assert %(py7)spy7N)r   r   sys
executablestrSTASH_AUDIT_PY
returncode
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgstderrAssertionError_format_explanationjsonloadsstdout)r-   r<   @py_assert1@py_assert4@py_assert3@py_format6@py_format8s          r"   _run_audit_jsonrX   C   s   ^^	^,hxPF
   !                    !"    .f.?.?-@ A==/	#     ::fmm$$r$   sourceapprovepr_verifiedidx_in_drop_listetidtask_idc                 b    | dk(  r|ryy| dk(  r|r|r||k(  ry|syy| dk(  r	|r|ry|syy| d	v ryy
)u:   
    spec §2 결정 흐름 reference implementation.
    pre-taskpoppeddry-run-popzfinish-task	preservedzother-filesdroppedzdry-run-drop)wip
quarantineunknownskipped rY   rZ   r[   r\   r]   r^   s         r"   decide_actionrk   U   s]     { '!11r$   entriesc           
      \   g }d}| D ]_  }|d   }|j                  d      }|d   }t        ||dd||      }	|dk(  r|dz  }|j                  ||||j                  d	d
      |	d       a t        j                  t
        j                        j                         ||rdndt        |       ||dS )uj   
    spec §2/§3 기반 dispatch 시뮬레이션.
    audit log 포맷(§4.2)에 맞는 dict 반환.
    r   rY   r^   indexFrj   rg      reason )rn   rY   r^   rp   actionapprovedzdry-run)timestamp_utcr^   approval_modestash_count_before	decisionsskipped_unknown_count)	getrk   appendr   nowr   utc	isoformatlen)
rl   rZ   r^   rw   rx   erY   r]   idxrr   s
             r"   simulate_dispatchr   y   s    
 I 8uuYj"
 Y!Q&!eeHb)
 	#4 "hll3==?'.I!'l!6 r$   c                     t               }t        |       |z  }|j                  d       t        j                  dd|g| d|       t        j                  g d| d|       y)u   
    메시지 없이 git stash push — git 이 'WIP on main: <SHA> <desc>' 형식 자동 생성.
    stash_audit.py 의 wip 패턴(^WIP on \w) 에 매칭됨.
    zwip contentr   r   Tr   )r   r2   r3   Nr4   )r-   r/   r   r5   s       r"   _stash_push_no_msgr      sS    
 *CNX%E	]#NNE5(+3ONN+3Or$   c               #      K   t               } t        | dd       t        | dd       t        | dd       t        | d       t        | dd	       |  t        j                  | d
       yw)u   
    unknown stash 3건 + wip 1건 + pre-task 1건 시드.
    총 5건.

    wip stash: 메시지 없이 git stash push → "WIP on main: <SHA> ..." 자동 생성
              → stash_audit.py 의 ^WIP on \w 패턴 매칭
    z#random unknown stash alpha no matchzu1.txtz'completely unrelated stash message betazu2.txtz"some garbage stash data gamma 9999zu3.txtzw1.txtzWIP: pre-task-2571 stash samplezp1.txtT)ignore_errorsN)r#   r6   r   shutilrmtree)r-   s    r"   mixed_quarantine_repor      sg       H ?JCXN>I x* ;XF
N
MM($/s   A'A)c                    t        |       }|j                  dg       }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t        |       d      d	z   d
|iz  }t        t        j                  |            dx}x}}y)u-   5건 stash 가 모두 감지되어야 한다.rl      r:   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sr~   r=   py1py3py6u   5건 시드 후 u   건 감지됨
>assert %(py8)spy8NrX   ry   r~   rF   rG   rH   rI   rJ   rK   rL   rN   rO   )r   datarl   @py_assert2@py_assert5rT   @py_format7@py_format9s           r"   !test_quarantine_total_stash_countr      s   01Dhhy"%Gw< 1 <1   <1                                3w<.6     r$   c           
         t        |       }|j                  dg       }|D cg c]  }|d   dk(  s| }}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
t        |       d|D cg c]  }|d   	 c}       dz   d|iz  }	t        t        j                  |	            dx}x}}yc c}w c c}w )u+   unknown 분류 건수가 3이어야 한다.rl   rY   rg      r:   r   r~   unknown_entriesr   u"   unknown 건수 기대: 3, 실제: z

sources: r   r   Nr   )
r   r   rl   r   r   r   r   rT   r   r   s
             r"   "test_quarantine_unknown_count_is_3r      sg   01Dhhy"%G")FQQx[I-EqFOF 1 1$   1                             $%    -S-A,B C*12QQx[23	5      G 3s   E>E><Fc                    t        |       }|j                  dg       }|D cg c]  }|d   dk(  s| }}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
t        |             dz   d|iz  }	t        t        j                  |	            dx}x}}yc c}w )u'   wip 분류 건수가 1이어야 한다.rl   rY   re   ro   r:   r   r~   wip_entriesr   u   wip 건수 기대: 1, 실제: r   r   Nr   )
r   r   rl   r   r   r   r   rT   r   r   s
             r"   test_quarantine_wip_count_is_1r      sD   01Dhhy"%G%>8)=1>K>{ q q    q                             !    )[)9(:;      ?
   E*E*c                    t        |       }|j                  dg       }|D cg c]  }|d   dk(  s| }}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
t        |             dz   d|iz  }	t        t        j                  |	            dx}x}}yc c}w )u,   pre-task 분류 건수가 1이어야 한다.rl   rY   r`   ro   r:   r   r~   pretask_entriesr   u#   pre-task 건수 기대: 1, 실제: r   r   Nr   )
r   r   rl   r   r   r   r   rT   r   r   s
             r"   "test_quarantine_pretask_count_is_1r      sE   01Dhhy"%G")GQQx[J-FqGOG 1 1$   1                             $%    .c/.B-CD      Hr   c                 L   t        |       }|j                  dg       }t        |dd      }|d   D cg c]  }|d   dk(  s| }}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t        |             dz   d|	iz  }
t        t	        j                  |
            dx}x}}|D ]  }|d   }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}} yc c}w )u9   unknown 3건 모두 action == preserved 이어야 한다.rl   F	task-2571rZ   r^   rw   rY   rg   r   r:   r   r~   unknown_decisionsr   u)   unknown decisions 수 기대: 3, 실제: r   r   Nrr   rc   z%(py1)s == %(py4)sr   py4u0   unknown stash action 기대: preserved, 실제: z
decision: 
>assert %(py6)sr   )rX   ry   r   r~   rF   rG   rH   rI   rJ   rK   rL   rN   rO   )r   r   rl   logr!   r   r   r   rT   r   r   @py_assert0rU   @py_format5s                 r"   %test_quarantine_unknown_all_preservedr      s   01Dhhy"%G
GUK
HC$'$4Qq(y8PQQ ! Q !Q&   !Q                !    !    "    &'    4C8I4J3KL      
{ 	
k 	
{k) 	
 	
{k 	
 	
 
	  	
 	
 
	 * 	
 	
  ?q{m L	
 	
 	
 	
 	
 	

	 Rs   H!H!c                    t        |       }|j                  dg       }t        |dd      }|d   D cg c]  }|d   dk(  s| }}|D ]  }|d   }d	}||k(  }|st        j                  d
|fd||f      t        j
                  |      t        j
                  |      dz  }	t        j                  d|d          dz   d|	iz  }
t        t        j                  |
            dx}x}} t        |dd      }|d   D cg c]  }|d   dk(  s| }}|D ]  }|d   }d	}||k(  }|st        j                  d
|fd||f      t        j
                  |      t        j
                  |      dz  }	t        j                  d|d          dz   d|	iz  }
t        t        j                  |
            dx}x}} yc c}w c c}w )uL   wip stash 는 action == preserved 이어야 한다 (항상, approve 무관).rl   Fr   r   rw   rY   re   rr   rc   r:   r   r   u.   wip dry-run action 기대: preserved, 실제: r   r   NTu/   wip approved action 기대: preserved, 실제: 	rX   ry   r   rF   rG   rK   rL   rN   rO   )r   r   rl   log_dryr!   wip_decisions_dryr   rU   r   r   r   log_approvedwip_decisions_approveds                r"   test_quarantine_wip_preservedr     s   01Dhhy"%G  LG$+K$8QqAhK5<PQQ 
{ 	
k 	
{k) 	
 	
{k 	
 	
 		  	
 	
 		 * 	
 	
  =Qx[MJ	
 	
 	
 	
 	
 	

 %WdKPL)5k)B[AakUZFZa[[# 
{ 	
k 	
{k) 	
 	
{k 	
 	
 		  	
 	
 		 * 	
 	
  >ak]K	
 	
 	
 	
 	
 	

 R \s   F7F7?F<F<c                    t        |       }|j                  dg       }t        |dd      }|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}||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}}yc c}w )uF   pre-task stash 는 dry-run 시 action == dry-run-pop 이어야 한다.rl   Fr   r   rw   rY   r`   ro   r:   r   r~   pretask_decisionsr   zassert %(py8)sr   Nr   rr   rb   r   r   u5   pre-task dry-run action 기대: dry-run-pop, 실제: r   r   )rX   ry   r   r~   rF   rG   rH   rI   rJ   rK   rN   rO   rL   )r   r   rl   r   r!   r   r   r   rT   r   r   r   rU   r   s                 r"   #test_quarantine_pretask_dry_run_popr   #  s   01Dhhy"%G
GUK
HC$'$4Rq(z8QRR !&Q&!Q&&&&!Q&&&&&&3&&&3&&&&&& &&& &&&!&&&Q&&&&&&&Q) ] )]:  )]    *    .;    @@QRS@TU]@^?_`      Ss   G:G:c                    t        |       }|j                  dg       }t        |dd      }|d   }d}||k(  }|st        j                  d|fd||f      t        j
                  |      t        j
                  |      d	z  }t        j                  d
|d          dz   d|iz  }t        t        j                  |            dx}x}}y)u   
    audit log 의 skipped_unknown_count == 3 검증.
    spec §4.2 포맷에서 unknown 건은 skipped_unknown_count 로 박제.
    rl   Fr   r   rx   r   r:   r   r   u)   skipped_unknown_count 기대: 3, 실제: r   r   Nr   )	r   r   rl   r   r   rU   r   r   r   s	            r"   *test_quarantine_skipped_unknown_count_is_3r   0  s    
 01Dhhy"%G
GUK
HC&' 1 '1,  '1    (    ,-    4C8O4P3QR     r$   c                    t        |       }|j                  dg       }t        |dd      }|d   D cg c]  }|d   dk(  s| }}|D ]  }|d   }d	}||k(  }|st        j                  d
|fd||f      t        j
                  |      t        j
                  |      dz  }	t        j                  d|d          dz   d|	iz  }
t        t        j                  |
            dx}x}} yc c}w )uh   
    APPROVE=1 이어도 unknown stash 는 preserved 여야 한다 (spec §3.1 — cleanup 금지).
    rl   Tr   r   rw   rY   rg   rr   rc   r:   r   r   u>   APPROVE=1 이어도 unknown action 기대: preserved, 실제: r   r   Nr   )r   r   rl   r   r!   r   r   rU   r   r   r   s              r"   3test_quarantine_unknown_preserved_even_with_approver   >  s     01Dhhy"%G
GT;
GC$'$4Qq(y8PQQ 
{ 	
k 	
{k) 	
 	
{k 	
 	
 		  	
 	
 		 * 	
 	
  MQx[MZ	
 	
 	
 	
 	
 	

 Rs   C+C+)-__doc__builtinsrH   _pytest.assertion.rewrite	assertionrewriterF   rP   r*   r   r   rA   r   r   r   pathlibr   pytest__file__resolveparentsWORKTREE_ROOTrD   rC   r#   dictr   r6   rX   boolrk   listr   r   fixturer   r   r   r   r   r   r   r   r   r   ri   r$   r"   <module>r      s     	   
  '  X&&(003*-==
 
$ _# _ _s _t _%c %d %$!!! ! 	!
 *! 4Z! 	!H(tDz (D (3 (4 (^
P 
P 
P 
P 0 0<
"
,

r$   