
    Yi["                        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d      Zedz  dz  ZdddZdd	Zdd
ZddZdddZd Zd Zy)u  tests/taskctl/test_self_approve.py
self-approve 차단 검증 (task-2467)

벨레스(개발6팀 테스터) 작성. 스바로그의 구현 완료 전 선작성(TDD).

케이스:
    1. test_self_approve_blocked — PR author == approver → ESCALATED + exit 1
    2. test_approve_with_different_human_passes — PR author != approver → HUMAN_APPROVED

환경 hook 요청 (스바로그 구현 측):
    - `TASKCTL_PR_AUTHOR_OVERRIDE` 환경변수: gh API 없이 PR author를 주입할 수 있는 hook.
      예) env["TASKCTL_PR_AUTHOR_OVERRIDE"] = "team-bot"
      → taskctl approve가 gh pr view 대신 이 값을 PR author로 사용.
    - 또는 `TASKCTL_SKIP_GH_VERIFY=1` 플래그로 gh 호출 전체 skip + 파라미터만으로 검증.
    )annotationsN)Pathz-/home/jay/workspace/.worktrees/task-2467-dev6scriptsz
taskctl.pyc                4   i t         j                  }t        |       |d<   |r|j                  |       | dz  dz  j	                  dd       | dz  dz  j	                  dd       | dz  dz  j	                  dd       | dz  d	z  j	                  dd       |S )
NWORKSPACE_ROOT.tasksstateT)parentsexist_okevidencememoryeventszorchestration-audit)osenvironstrupdatemkdir)tmp_path	extra_envenvs      6/home/jay/workspace/tests/taskctl/test_self_approve.py_isolated_workspacer      s    
RZZ.CMC

97"))$)F:%,,TD,I8#**4$*G0077t7TJ    c                Z    t        j                  dt        t              g| z   dd|d      S )Npython3T   )capture_outputtextr   timeout)
subprocessrunr   TASKCTL)argsr   s     r   _runr$   )   s-    >>	CL!D($C r   c                   | dz  dz  | 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                               S )Nr   r	   .jsonu   state 파일 없음: zC
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}p)py0py2py4)exists
@pytest_ar_format_assertmsg@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationjsonloads	read_text)r   task_idr'   @py_assert1@py_assert3@py_format5s         r   _stater;   0   s    8g%7)5(99A8828:2:22.qc2222222122212228222:222222::akkm$$r   c                   ddl }| dz  dz  | dz  }t        j                  |j                               }||d<   |j	                  dd       |j                         D ci c]  \  }}|dk7  s|| }}}t        j                  |dd	d
      }	|j                  |	j                  d            j                         }
|
|d<   |j                  t        j                  |dd             yc c}}w )u<   테스트 전용: state 강제 설정 + 체크섬 재계산.r   Nr   r	   r&   current_state	_checksumFT),:)ensure_ascii	sort_keys
separatorszutf-8   )rA   indent)hashlibr4   r5   r6   popitemsdumpssha256encode	hexdigest
write_text)r   r7   target_staterF   r'   r	   kvpayloadcanonchecksums              r   _force_state_with_checksumrT   6   s    8g%7)5(99AJJq{{}%E)E/	IIk4  %B1k1Aq!tBGBJJwUdzZE~~ell734>>@H!E+LLEa@A	 Cs    C*.C*c           
         d|gd|gd|gd|gd|dt        |      gfD ]  }t        ||      j                  dk7  s y t        | |d	       t	        | |      }|d
   dv S )ut   VERIFIED/GUARD_PASS 상태까지 강제 진입.

    Returns True if successfully in VERIFIED/GUARD_PASS state.
    initdispatchackr!   zpr-openz--prr   F
GUARD_PASSr=   >   VERIFIEDrY   )r   r$   
returncoderT   r;   )r   r   r7   	pr_numbercmdr	   s         r   _setup_to_verifiedr^   E   s    
 !J#8 5'"2GVS^<>  S>$$)	 x,?8W%E!%???r   c                   d}t        | |dd      }d}t        | ||d      }|st        j                  d       t	        d	|d
|g|      }|j
                  dk7  r;t        | |      }|d   }h d}||v }	|	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z   d|
iz  }t        t        j                  |            dx}	}| dz  dz  |z  dz  }|j!                         rJt#        j$                  |j'                               }|j(                  }	d} |	|      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      t        j                  |      t        j                  |      t        j                  |      dz  }t        j                  d|j)                  d             d z   d!|iz  }t        t        j                  |            dx}	x}x}x}}|j)                  d"d      }g }	d#}|j*                  } |       }||v }|}|s8d$}|j*                  } |       }||v }|}|sd	}|j*                  } |       }||v }|}|sxt        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-                  |       |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.                  |	d0      i z  }$t        j                  d1|       d2z   d3|$iz  }%t        t        j                  |%            dx}x}	x}x}x}x}x}x}x}x}x}x}x}}yyt        | |      }|d   d4k(  rt        j0                  d5       yy)6u   PR author == approver → ESCALATED 전이 + exit 1 + approval.json result=FAIL.

    구현 hook: TASKCTL_PR_AUTHOR_OVERRIDE 환경변수로 PR author 주입.
    hook 미구현 시 gh CLI 호출 실패로 graceful skip (ESCALATED 또는 exit 1).
    human-reviewer TASKCTL_PR_AUTHOR_OVERRIDEGH_TOKENr   ztask-selfapprove-01i  r\   1   GUARD_PASS 상태 진입 실패 — 환경 문제approve--byr   r=   >   PR_OPENrZ   	ESCALATEDrY   in)z%(py0)s in %(py3)scurrent)r(   py3u+   self-approve 차단 후 예상 외 상태: z
>assert %(py5)spy5Nr   r   approval.jsonresultFAIL==)zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} == %(py9)sevr(   r)   r*   py6py9u(   approval.json result가 FAIL이 아님: 
>assert %(py11)spy11reasonselfauthor)zD%(py3)s in %(py9)s
{%(py9)s = %(py7)s
{%(py7)s = %(py5)s.lower
}()
})ro   rp   py7ry   z%(py11)s)zJ%(py14)s in %(py20)s
{%(py20)s = %(py18)s
{%(py18)s = %(py16)s.lower
}()
})py14py16py18py20z%(py22)spy22)zJ%(py25)s in %(py31)s
{%(py31)s = %(py29)s
{%(py29)s = %(py27)s.lower
}()
})py25py27py29py31z%(py33)spy33   u4   approval.json reason이 self-approve 관련 없음: z
>assert %(py36)spy36HUMAN_APPROVEDuq   TASKCTL_PR_AUTHOR_OVERRIDE hook 미구현 — MVP는 PR author 검증 없이 approve 허용 (스바로그 대기))r   r^   pytestskipr$   r[   r;   r,   _call_reprcomparer.   r/   r0   r1   r-   r2   r3   r+   r4   r5   r6   getlowerappend_format_boolopxfail)&r   approverr   r7   okprocr	   rn   @py_assert2r8   @py_format4@py_format6approval_evrv   r9   @py_assert5@py_assert8@py_assert7@py_format10@py_format12r|   @py_assert6@py_assert4@py_assert0@py_assert13@py_assert17@py_assert19@py_assert15@py_assert24@py_assert28@py_assert30@py_assert26@py_format21@py_format23@py_format32@py_format34@py_format35@py_format37s&                                         r   test_self_approve_blockedr   Z   s{     H
h&.3 C $G	Hc7c	BBGH GVX6<D !x)(L 	
wLL 	
 	
wL 	
 	
	6	
 	
   	
 	
 		  	
 	
 		 M 	
 	
  :'C	
 	
 	
 	
 	

 )J6@?RK1134B66 ( 6(# v #v-  #v  v     I   I   I #  I $  I (.    ;266(;K:LM      VVHb)F6 V\\ \^ 6^+ x 6<< <> x>/I Y Z`ZfZf ZfZh YZhMh   6^  I   v   $  I $  I *  I ,   v x>  I 08  v   <B  I <B  I <H  I <J   v YZh  I NW  v   [a  I [a  I [g  I [i   v    GvhO          x)!%55LL D 6r   c                
   d}d}t        | |dd      }d}t        | ||d      }|st        j                  d	       t	        d
|d|g|      }|j
                  dk(  rt        | |      }|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}
}	|j                  }d}	 ||	      }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                   |      rt        j                  |      ndt        j                  |      t        j                  |	      t        j                  |      t        j                  |      dz  }t        j                  d      dz   d|iz  }t        t        j                  |            dx}x}	x}x}}|j                  dg       }|D cg c]  }|j                  d       d!v s| }}|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&z  d'z  |z  d(z  }|j#                         rt%        j&                  |j)                               }|j                  }d)}	 ||	      }h d*}||v }|st        j                  d+|fd,||f      d-t        j                         v st        j                   |      rt        j                  |      nd-t        j                  |      t        j                  |	      t        j                  |      t        j                  |      dz  }t        j                  d.|j                  d)             dz   d|iz  }t        t        j                  |            dx}x}	x}x}}d/|v rd0|v r|d/   }|d0   }	||	k7  }
|
st        j                  d1|
fd2||	f      t        j                  |      t        j                  |	      dz  }t        j                  d3|d/          dz   d|iz  }t        t        j                  |            dx}x}
}	yyyy|j*                  }d4|v sd5|v rt        j                  d6       yd|v sd7|j-                         v rt        j.                  d8       yt        j.                  d9|dd:         yc c}w );u   PR author != approver → HUMAN_APPROVED 정상 전이.

    TASKCTL_PR_AUTHOR_OVERRIDE=bot-user, approver=human-reviewer → 승인 허용.
    또는 MVP에서 --by 파라미터만으로 승인 (GUARD_PASS → HUMAN_APPROVED).
    zteam-botr`   ra   rb   re   ztask-selfapprove-02i  rf   rg   rh   ri   r   r=   r   rt   )z%(py1)s == %(py4)s)py1r*   u-   approve 성공 후 HUMAN_APPROVED가 아님: z
>assert %(py6)srx   Nhuman_approvedT)is)zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} is %(py9)sr	   rw   u"   human_approved 플래그 미설정rz   r{   transitionsto>   r   u#   HUMAN_APPROVED 전이 이력 없음z
>assert %(py0)sr(   human_approved_transitionr   r   rq   rr   >   OKPASSAPPROVEDrl   )zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} in %(py9)srv   u   approval.json result 이상: 	pr_authorr   )!=)z%(py1)s != %(py4)su6   approval.json에 self-approve가 기록됨: pr_author=rY   rZ   uJ   approve 명령이 GUARD_PASS 상태를 인식 못함 (상태명 불일치)byu1   --by 파라미터 미구현 (스바로그 대기)u   approve 실패 원인 불명: i,  )r   r^   r   r   r$   r[   r;   r,   r   r1   r-   r2   r3   r   r.   r/   r0   r+   r4   r5   r6   stderrr   r   )r   
bot_authorhuman_approverr   r7   r   r   r	   r   r9   r   r:   @py_format7r8   r   r   r   r   r   r   tr   @py_format1r   rv   r   s                             r   (test_approve_with_different_human_passesr      s    J%N
h&03 C $G	Hc7c	BBGH GV^<cBD !x)_% 	
)9 	
%)99 	
 	
%)9 	
 	
 		 & 	
 	
 		 *: 	
 	
  <E/<R;ST	
 	
 	
 	
 	
 yyX)Xy)*XdX*d2XXX*dXXXXXXuXXXuXXXyXXX)XXX*XXXdXXX4XXXXXXXX iir2"%
aeeDk5G&GA%
! %
 )OO*OOOOOOO(OOO(OOOOO )J6@?RK1134B66 ( 6(# 'A #'AA  #'A  v     I   I   I #  I $  I (B    0x0@/AB      b Z2%5+ "Z. .8   .  	 '  	 +9     MRP[_L]^      &6    6!Z6%9KKdev!7LLLMLL9&#,HI3%
s   .UU)N)r   r   r   zdict | Nonereturndict)r#   z	list[str]r   r   r   zsubprocess.CompletedProcess)r   r   r7   r   r   r   )r   r   r7   r   rN   r   r   None)r   )
r   r   r   r   r7   r   r\   intr   bool)__doc__
__future__r   builtinsr.   _pytest.assertion.rewrite	assertionrewriter,   r4   r   r    pathlibr   r   	WORKSPACEr"   r   r$   r;   rT   r^   r   r    r   r   <module>r      sc    #    	   @A	
i
,
.	%B@*2r8Jr   