
    i+4                       d Z ddl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	Z	 ed      Z
e
dz  dz  Zddd	 	 	 dd	Ze	j                  d
        ZddZd Zd Zd Zd Zd Zd Zd ZdddZd Zd Zd Zd Zd Zd Zy)u  tests/test_taskctl.py — taskctl MVP 회귀 테스트 (task-2449)

8가지 케이스 + bypass:
    정상 (3):
        1. test_normal_verify_runs_and_collects_evidence
        2. test_normal_approve_transitions_to_human_approved
        3. test_normal_merge_dry_run_completes
    차단 (5):
        4. test_blocked_cancelled_task_merge_exits_1
        5. test_blocked_main_direct_push_via_pre_push_hook_exits_1
        6. test_blocked_no_direct_gh_pr_merge_in_codebase  (grep 검증)
        7. test_blocked_guard_sh_fail_blocks_merge
        8. test_blocked_human_approval_missing_blocks_merge
    bypass (1):
        9. test_bypass_records_evidence_and_proceeds
    구조 검증:
        10. test_help_lists_all_subcommands
        11. test_state_file_checksum_detects_tampering
    )annotationsN)Pathz/home/jay/workspacescriptsz
taskctl.py)envcwdc                    t         j                  j                         }| r|j                  |        t	        j
                  t        j                  t        t              g|ddd|t        |xs t                    S )NT<   )capture_outputtexttimeoutr   r   )osenvironcopyupdate
subprocessrunsys
executablestrTASKCTL	WORKSPACE)r   r   argsbase_envs       C/home/jay/workspace/.worktrees/task-2460-dev6/tests/test_taskctl.py_run_taskctlr   '   s]    zz H
>>	W--$#c.Y/     c                &   | dz  }|j                          |dz  dz  j                  d       |dz  j                  t        dz         |dz  j                  d       |dz  d	z  j                  d       |j                  d
t	        |             |S )uB   별도 .tasks/state 디렉토리로 실행되는 격리된 환경.fake_workspace.tasksstateT)parentsr   memoryexist_okeventsWORKSPACE_ROOT)mkdir
symlink_tor   setenvr   )tmp_pathmonkeypatchr   s      r   isolated_stater,   3   s      00Nh(///=i++I	,ABh%%t%4h)00$0?'^)<=r   c                    | dz  dz  | dz  S )Nr   r    .json )	workspacetask_ids     r   _state_filer2   @   s    x')wiu,===r   c                     t        j                  t        j                  ddt	        t
              gddd      } | j                  dk(  sJ d| j                          y )N-m
py_compileT   )r
   r   r   r   zpy_compile FAIL: )r   r   r   r   r   r   
returncodestderr)procs    r   test_taskctl_py_compiler:   I   sP    >>	|S\:$D ??aB#4T[[M!BBr   c                     t        d      } | j                  dk(  sJ | j                  }dD ]  }||v rJ d| d        y )Nz--helpr   )initdispatchackr   pr-openverifyapprovemergecancelfailstatusu   --help 출력에 'u   ' 누락)r   r7   stdout)r9   outcmds      r   test_help_lists_all_subcommandsrI   Q   sT    !D??a
++C@ >cz=/uH==z>r   c                   d}t        d|      }|j                  dk(  sJ t        | |      }|j                         sJ t	        j
                  |j                               }d|d<   |j                  t	        j                  |             t        d|      }|j                  dk7  sJ d|j                  j                         v sd|j                  j                         v sJ y y )	Nztask-tamper-99r<   r   HUMAN_APPROVEDcurrent_staterE   checksumtampered)r   r7   r2   existsjsonloads	read_text
write_textdumpsr8   lower)r,   r1   r9   pdatas        r   *test_state_file_checksum_detects_tamperingrX   Z   s    G(D??aNG,A88::::akkm$D,DLLD!"'*D??a**,,
dkk>O>O>Q0QQQ0Q,r   c                   d}t        d|      j                  dk(  sJ t        d|      j                  dk(  sJ t        d|      j                  dk(  sJ t        d|      j                  dk(  sJ t        d|dd	      j                  dk(  sJ t        d
|d       t        j                  t	        | |      j                               }|d   }|d   dv sJ |d   dv sJ d
|d   v sJ |d   dv sJ |d   dk(  sJ y)u  정상 1: verify 명령이 evidence를 수집하고 state 파일을 갱신한다.

    fake workspace는 git repo도 PR도 없으므로 guard.sh / qc_report_guard 모두
    FAIL이 예상된다. 핵심 검증 항목:
        - evidence.guard_sh_result / qc_report_guard_result 필드 존재
        - evidence.exit_codes['verify'] 기록
        - PR_OPEN 또는 GUARD_PASS 상태 유지 (terminal로 빠지지 않음)
    ztask-2449-normal-1r<   r   r=   r>   r   r?   --pr999r@   z	--machineevidenceguard_sh_result>   FAILPASSqc_report_guard_result
exit_codesrL   >   PR_OPEN
GUARD_PASS	pr_numberi  Nr   r7   rP   rQ   r2   rR   )r,   r1   r    evs       r   -test_normal_verify_runs_and_collects_evidencerg   m   s+    #G(33q888
G,771<<<w'22a777w'22a777	7FE:EEJJJ7K0JJ{>7;EEGHE	z	B $4444&'+;;;;r,''''!%>>>>k?c!!!r   c                R   d}t        d|      j                  dk(  sJ dD ]  }t        ||      j                  dk(  rJ  t        d|dd      j                  dk(  sJ t        | |      }|j                         sJ t        d|      }|j                  dk7  sJ d	|j                  v sJ y
)u   정상 2: PR_OPEN 상태에서 approve 시도 → 차단 (GUARD_PASS 필요).

    MVP에서는 verify 외 GUARD_PASS 진입 경로가 없으므로,
    PR_OPEN→approve는 차단되어야 한다는 점을 검증한다.
    ztask-2449-normal-2r<   r   r=   r>   r   r?   rZ   1rA   rc   N)r   r7   r2   rO   r8   )r,   r1   rH   
state_pathr9   s        r   1test_normal_approve_transitions_to_human_approvedrl      s     #G(33q888) :C)44999:	7FC8CCqHHH^W5J	7+D??a4;;&&&r   c                    d}t        d|      j                  dk(  sJ dD ]  }t        ||      j                  dk(  rJ  t        d|dd      j                  dk(  sJ t        d|d	      }|j                  dk7  sJ y
)u;  정상 3: HUMAN_APPROVED 상태에서 merge --dry-run → MERGED → DONE.

    taskctl 자체로 GUARD_PASS 전이는 verify를 거쳐야 하므로,
    monkeypatch로 guard.sh / qc_report_guard 호출을 우회하는 헬퍼 사용.
    여기서는 단계별 명령이 정상 동작하는지만 확인한다.
    ztask-2449-normal-3r<   r   ri   r?   rZ   rj   rB   	--dry-runN)r   r7   r,   r1   rH   r9   s       r   #test_normal_merge_dry_run_completesrp      s     #G(33q888) :C)44999:	7FC8CCqHHH+6D??ar   c                   d}t        d|      j                  dk(  sJ t        d|      j                  dk(  sJ t        d|d      }|j                  dk(  sJ t        j                  t	        | |      j                               }|d   d	k(  sJ y
)u0   차단 4: CANCELLED state task → merge exit 1.ztask-2449-block-4r<   r   rC   rB   rn      rL   	CANCELLEDNre   )r,   r1   r9   r    s       r   )test_blocked_cancelled_task_merge_exits_1rt      s    !G(33q888'*55:::+6D??aJJ{>7;EEGHE![000r   c                   | j                  d       t        j                  ddd|gt        |       dd       t        j                  g dt        |       dd       t        j                  g d	t        |       dd       | d
z  j	                  d       t        j                  g dt        |       dd       t        j                  g dt        |       dd       y)uE   git init + dummy commit (HEAD가 존재해야 hook이 정상 평가).Tr#   gitr<   z-b)r   r
   check)rv   configz
user.emailz
test@local)r   rw   r
   )rv   rx   z	user.nametestREADMEztest
)rv   add.)rv   commitr4   r<   z--no-verifyN)r'   r   r   r   rS   )pathbranchs     r   _init_fake_repor      s    JJJNNE640c$i"&d4 NN@4yTCNN94yTC	H_  *NN&CI"&d4NNA4yTCr   c                .   t         dz  dz  dz  }|j                         sJ | dz  }t        |d       t        j                  dt        |      gt        |      dd	d	d
      }|j                  dk(  sJ d|j                          d|j                  v sJ y)u   차단 5: pre-push hook 호출 시 main 브랜치 → exit 1.

    실제 git push 환경 시뮬레이션이 아닌, pre-push 스크립트 직접 호출.
    r   	git-hookspre-push	fake_repomainr   bash T   r   inputr
   r   r   rr   u    main direct push 거부 실패: main direct push prohibitedNr   rO   r   r   r   r   r7   r8   )r*   hookr   r9   s       r   7test_blocked_main_direct_push_via_pre_push_hook_exits_1r      s    
 y ;.;D;;==;&IIf->>	T	N$	D ??aQ#CDKK=!QQ(DKK777r   c                    t        j                  g dt        t              ddd      } | j                  dk(  ry| j
                  j                         }g }d}d}|D ]U  }d	|v r|j                  d	d      d
   n|t        fd|D              r3j                  |      rE|j                  |       W |r
J d|        y)u   차단 6: 코드베이스에 'gh pr merge' 직접 호출이 0건.

    예외:
        - scripts/taskctl.py 자체 (유일한 머지 진입점)
        - tests/ (본 파일 등 검증 코드)
        - memory/, dispatch/, *.md (문서)
    )rv   grepz-nzgh pr mergeTr6   )r   r
   r   r   rr   N)zscripts/taskctl.py:ztests/zmemory/z	dispatch/zscripts/anu_confirm_bot/)z.mdz.txtz.logz.jsonlr.   :r   c              3  @   K   | ]  }j                  |        y w)N)
startswith).0rV   r~   s     r   	<genexpr>zAtest_blocked_no_direct_gh_pr_merge_in_codebase.<locals>.<genexpr>   s     <atq!<s   u2   'gh pr merge' 직접 호출 발견 (taskctl 외): )r   r   r   r   r7   rF   
splitlinessplitanyendswithappend)r9   lines
violationsallowed_prefixesallowed_extlnr~   s         @r   .test_blocked_no_direct_gh_pr_merge_in_codebaser      s     >>,	N$D !KK""$EJ =K &)RirxxQ"R<+;<<==%"  
<ZLI>zr   c                p   d}t        d|      j                  dk(  sJ dD ]  }t        ||      j                  dk(  rJ  t        d|dd      j                  dk(  sJ t        d|d	      }|j                  d
k(  sJ d|j                  v s.d|j                  v sd|j                  j                         v sJ yyy)uR   차단 7: guard.sh FAIL 상태 → merge exit 1 (HUMAN_APPROVED 미달과 동시).ztask-2449-block-7r<   r   ri   r?   rZ   rj   rB   rn   rr   rK   u   차단blockedN)r   r7   r8   rU   ro   s       r   'test_blocked_guard_sh_fail_blocks_merger     s    !G(33q888) :C)44999:	7FC8CCqHHH+6D??at{{*h$++.EVZVaVaVgVgViIiiiIi.E*r   c                    d}t        d|      j                  dk(  sJ t        d|d      }|j                  dk(  sJ d|j                  v sJ y)	u1   차단 8: HUMAN_APPROVED 미달 → merge exit 1.ztask-2449-block-8r<   r   rB   rn   rr   rK   N)r   r7   r8   )r,   r1   r9   s      r   0test_blocked_human_approval_missing_blocks_merger     sT    !G(33q888+6D??at{{***r   c                   d}t        d|      j                  dk(  sJ dD ]  }t        ||      j                  dk(  rJ  t        d|dd      j                  dk(  sJ t        d|d	d
di      }|j                  dk(  sJ d|j                          d|j                  v sJ t        j                  t        | |      j                               }|d   d   du sJ |d   d   J |d   d   J |d   dk(  sJ |d   d   J y)uR   bypass 9: TASKCTL_BYPASS=1 set 시 evidence에 bypass=true 기록 + stderr 경고.ztask-2449-bypassr<   r   ri   r?   rZ   777rB   rn   TASKCTL_BYPASSrj   )r   u   bypass dry-run 머지 실패: zBYPASS USEDbypassusedTtsNactorrL   DONEr\   merge_timestamp)r   r7   r8   rP   rQ   r2   rR   )r,   r1   rH   r9   r    s        r   )test_bypass_records_evidence_and_proceedsr   "  sA    G(33q888) :C)44999:	7FE:EEJJJ+-s35D??aO#A$++!OODKK'''JJ{>7;EEGHE?6"d***?4 ,,,?7#///!V+++./;;;r   c                   t         dz  dz  dz  }|j                         sJ | dz  }t        |d       d}t        j                  dt        |      gt        |      |d	d	d
      }|j                  dk(  sJ d|j                  v sJ y)uS   다른 브랜치에서 origin/main 으로 push 시도 → refspec 검사로 차단.r   r   r   r   z	feature/xr   z4refs/heads/feature/x abc123 refs/heads/main 0000000
r   Tr   r   rr   r   Nr   )r*   r   r   refspec_inputr9   s        r   !test_pre_push_refspec_blocks_mainr   ;  s    y ;.;D;;==;&IIk2KM>>	T	N$	D ??a(DKK777r   )r   r   r   zdict | Noner   zPath | Nonereturnzsubprocess.CompletedProcess)r0   r   r1   r   r   r   )r   )r~   r   r   r   r   None)__doc__
__future__r   rP   r   r   r   pathlibr   pytestr   r   r   fixturer,   r2   r:   rI   rX   rg   rl   rp   rt   r   r   r   r   r   r   r   r/   r   r   <module>r      s   & #  	  
  &'	
i
,
. 15$(	!	-H	 	 	>C>R&"4'& ,1C"8&$Nj+<28r   