
    -iCQ                       d Z ddl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edz  dz  Zedz  dz  Zd%d	Zd&d
Z	 	 d'	 	 	 	 	 	 	 d(dZd)dZdddd	 	 	 	 	 	 	 	 	 	 	 	 	 d*dZd+dZej*                  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"d Z#d  Z$d! Z%d" Z&d# Z'd$ Z(y)-u  tests/verify/test_taskctl_verify.py — taskctl_verify.py 11 검사 시나리오 테스트.

task-2459 Phase 2-C / dev5팀 닌기르수 (테스터).

scripts/taskctl_verify.py 의 11 개 검사를 임시 worktree + lock + task md 로
재현하여 PASS / FAIL / WARN / N/A 동작을 검증.

원칙:
  - 모듈 코드 read-only.
  - subprocess 로만 호출.
  - workspace 와 cwd 는 독립적 — `.worktrees/<id>-<bot>` 경로 패턴을 cwd 로 사용.
    )annotationsN)Path   scriptsztaskctl_verify.pymixed_commit_detector.pyc                N    t        j                  dg|t        |       ddd      S )NgitTFcwdcapture_outputtextcheck)
subprocessrunstr)r   argss     Q/home/jay/workspace/.worktrees/task-2459-dev5/tests/verify/test_taskctl_verify.py_gitr       s(    >>	CHTE     c                    | dz  dz  | dz  }|j                   j                  dd       dj                  d |D              }|j                  d| d	| d
d       |S )u2   task md 파일을 yaml 블록 포함하여 작성.memorytasks.mdTparentsexist_ok
c              3  (   K   | ]
  }d | d  yw)z    - ""N ).0xs     r   	<genexpr>z _make_task_md.<locals>.<genexpr>*   s     =XaS+=s   # z&

```yaml
allowed_resources:
  paths:
z
```
utf-8encoding)parentmkdirjoin
write_text)	workspacetask_idallowedp
paths_yamls        r   _make_task_mdr1   &   s~    Hw&G9C8AHHNN4$N/=W==JLLwi 
  	   
 Hr   c                   | dz  }|j                          t        dddd|       t        ddd	|       t        dd
d|       t        ddd|       |dz  dz  }|j                  d       || dz  j                  dd       |dz  dz  j                  d       |dz  }|j                  d       |dz  }|j                  t        j	                  d      d       t        ||g d       |dz  | d| z  }|j                  d       |dz  j                  dd       t        dd |       t        d!dd"d#|       t        d$d%|      j                  j                         }t        d&d'||       d(| d| }	t        d)dd|	|       |d*z  }
|
j                  d+       |
d,z  j                  d-d       t        dd.|       t        d!dd"d/| d0|       |||||	|d1S )2u  테스트용 전체 셋업.

    Layout (workspace 자체가 git repo, .worktrees 는 같은 repo 내 하위 디렉토리):
      tmp_path/
        workspace/                       <- WORKSPACE_ROOT, git repo
          .git/
          .tasks/locks/<task>.lock
          memory/tasks/<task>.md
          memory/events/
          scripts/mixed_commit_detector.py  (실제 detector 복사)
          src/                            (allowed_resources 매치용)
          .worktrees/<task>-<bot>/        <- cwd. 같은 repo 의 하위 디렉토리.

    git worktree 분리 모킹 대신 단일 repo 의 subdir 로 구성.
    git 명령은 어떤 cwd 에서도 같은 repo 를 인식한다.
    r,   init-q-bmainr   configz
user.emailztest@example.comz	user.nametesterzcommit.gpgsignfalse.taskslocksT)r   .lockzok
r%   r&   r   eventsr   r   )zsrc/**z	memory/**ztests/**z
scripts/**z	.tasks/**z
.worktrees-z.keepzkeep
add-Acommit-mzbase: initial setupz	rev-parseHEADz
update-refzrefs/remotes/origin/mainztask/checkoutsrc)r   z	module.pyz# implementation
zsrc/module.py[z] feat: add module)r,   worktreer-   botbranchbase_sha)r)   r   r+   DETECTOR_SRC	read_textr1   stdoutstrip)tmp_pathr-   rI   r,   r<   scripts_dirdetector_dstrH   rK   rJ   src_dirs              r   setup_full_reporT   9   s7   * ;&IOO 	tV3<!3C;i8#W)<  7*E	KKK
y**6G*D H$++D+9
 i'Kd#!;;LL22G2DwW F <'WIQse*<<HNN4N ##Hw#? 	)$44)D KY7>>DDFH18K WIQse$FT4Y7 %GMM4M {&&';g&NY/47)+=>IN  r   c                @    t        dd|        t        ddd||        y)u:   현재 staged/untracked 모두 commit (dirty_tree 정리).r@   rA   r7   rB   r4   rC   N)r   )r,   messages     r   _commit_allrW      s    )$4wI6r   Tr    )rI   	json_modeextrac                   t         j                  t        t              | dt        |      g}|r|d|gz  }|r|dgz  }|t	        |      z  }t        j                  |t        |      ddd      S )Nz--workspacez--botz--jsonTFr
   )sys
executabler   VERIFYlistr   r   )r-   rI   r,   r   rX   rY   cmds          r   
run_verifyr`      sn     >>3v;I
OC
~z4;C>>S$T r   c                    | j                   sJ d| j                          t        j                  | j                         S )Nu   stdout 비어있음: stderr=)rN   stderrjsonloads)ress    r   _parse_payloadrf      s2    ::B5cjj\BB:::cjj!!r   c                    t        |       S )N)rT   )rP   s    r   
full_setuprh      s    8$$r   c                `   | }t        |d   |d   |d   |d         }t        |      }|d   dk(  s"J d|d	    d
|d   j                  d              |j                  dk(  sJ dD ]  }|d	   |   dk(  rJ | d|d	   |            |d	   d   dk(  sJ |d	   d   dk(  sJ |d	   d   dk(  sJ y)uB   모든 필수 검사 PASS, 옵션은 N/A → overall=PASS, exit 0.r-   rI   r,   rH   rI   r,   r   overallPASSzoverall != PASS
results=results
fail_reasons=detailsfail_reasonsr   )
start_lockbranch_matchworktree_pathcancelled_check
dirty_treechanged_pathsscope_matrixmixed_commitu    != PASS — actual handoff_chainN/Aqc_report_guardguard_shN)r`   rf   get
returncode)rh   sre   payloadchks        r   test_all_pass_baseliner      s)   A
	)!E(an!J-C S!G9' 
#GI$6#7 8	*..~>?	A' >>Q	 
 y!#&&0 	
e'	(:3(?'@A	
0
 9o.%7779/0E9999j)U222r   c                    | }|d   dz  dz  |d    dz  j                          t        |d   |d   |d   |d         }t        |      }|d	   d
   dk(  sJ |d   dk(  sJ |j                  dk(  sJ y)u'   1. start_lock FAIL: lock 파일 삭제.r,   r;   r<   r-   r=   rI   rH   rj   rm   rq   FAILrk      N)unlinkr`   rf   r~   rh   r   re   r   s       r   test_fail_start_lockr      s    A{^h(a	l^5+AAIIK
	)!E(an!J-C S!G9l+v5559'''>>Qr   c                    | }t        dddd|d          t        |d   |d   |d	   |d   
      }t        |      }|d   d   dk(  sJ |d   dk(  sJ |j                  dk(  sJ y)u4   2. branch_match FAIL: 다른 브랜치 체크아웃.rE   r4   r5   zwrong-branchrH   r7   r-   rI   r,   rj   rm   rr   r   rk   r   N)r   r`   rf   r~   r   s       r   test_fail_branch_matchr      s    AT4Qz]C
	)!E(an!J-C S!G9n-7779'''>>Qr   c                    | }|d   dz  dz  |d    dz  }|j                  dd       t        |d   |d	   |d   |d
         }t        |      }|d   d   dk(  sJ |d   dk(  sJ |j                  dk(  sJ y)u2   3. cancelled_check FAIL: .cancelled 마커 생성.r,   r   r>   r-   z
.cancelledzcancelled by chairman
r%   r&   rI   rH   rj   rm   rt   r   rk   r   N)r+   r`   rf   r~   )rh   r   cancelre   r   s        r   test_fail_cancelled_checkr      s    A{^h&1q|nJ4OOF
/'B
	)!E(an!J-C S!G9/0F:::9'''>>Qr   c                
   | }|d   dz  j                  dd       t        |d   |d   |d   |d   	      }t        |      }|d
   d   dk(  sJ |d   dk(  sJ |j                  dk(  sJ t	        d |d   d   D              sJ y)u,   4. dirty_tree FAIL: untracked 파일 추가.rH   	stray.txtz
untracked
r%   r&   r-   rI   r,   rj   rm   ru   r   rk   r   c              3  $   K   | ]  }d |v  
 yw)r   Nr    )r!   fs     r   r#   z'test_fail_dirty_tree.<locals>.<genexpr>  s     KA{aK   ro   dirty_filesN)r+   r`   rf   r~   anyr   s       r   test_fail_dirty_treer   	  s    Az][ ,,]W,M
	)!E(an!J-C S!G9l+v5559'''>>QK);M)JKKKKr   c                   | }|d   dz  j                          |d   dz  dz  j                  dd       t        dd|d   	       t        d
ddd|d    d|d   	       t        |d   |d   |d   |d         }t	        |      }|d   d   dk(  sJ |d   dk(  sJ |j
                  dk(  sJ |d   d   }t        d |D              sJ y)u3   5. scope_matrix FAIL: allowed 외 경로에 commit.rH   	forbiddenzx.pyx
r%   r&   r@   forbidden/x.pyr7   rB   r4   rC   rG   r-   z] add forbiddenrI   r,   rj   rm   rw   r   rk   r   ro   scope_violationsc              3  $   K   | ]  }d |v  
 yw)r   Nr    )r!   vs     r   r#   z)test_fail_scope_matrix.<locals>.<genexpr>*  s     91$9r   N)r)   r+   r   r`   rf   r~   r   )rh   r   re   r   
violationss        r   test_fail_scope_matrixr     s   Az][ '')z][ 6)55eg5N a
m4$!I,?jM 	)!E(an!J-C S!G9n-7779'''>>Q#$67J9j9999r   c                    | }t        |d   |d   |d   |d         }t        |      }|d   d   dk(  sJ |d   d	   dk(  sJ |d   d
   dk(  sJ |d   dk(  sJ |j                  dk(  sJ y)uL   handoff/guard.sh/qc 보고서 부재 → 옵션 검사는 N/A, overall PASS.r-   rI   r,   rH   rj   rm   ry   rz   r{   r|   rk   rl   r   N)r`   rf   r~   r   s       r   $test_optional_checks_na_when_missingr   1  s    A
	)!E(an!J-C S!G9o.%7779/0E9999j)U2229'''>>Qr   c                   | }|d   dz  dz  |d    dz  }|j                  d|d    dd	       t        |d   d
|d    d       t        |d   |d   |d   |d         }t        |      }|d   d   dk(  s"J d|d    d|d   j	                  d              d|d   j                         vsJ |d   dk(  sJ |j                  dk(  sJ y)u  WARN 시나리오: task md 에 ``allowed_resources`` 블록 부재 (구버전 호환) →
    scope_matrix WARN, FAIL 0건 → overall=WARN, exit 2.

    spec 4.1 우선순위: FAIL 0 + WARN 1+ → exit 2 / overall=WARN.
    Codex 사전 검증 후 정책 변경: task md 부재/읽기 실패/paths 빈 리스트 = FAIL,
    allowed_resources 블록 자체가 없는 구버전 task md 만 WARN.
    r,   r   r   r-   r   r$   z2

legacy task md without allowed_resources block.
r%   r&   rG   z] downgrade task md (legacy)rI   rH   rj   rm   rw   WARNzresults=rn   ro   rp   r   rk   r   N)r+   rW   r`   rf   r}   valuesr~   rh   r   task_mdre   r   s        r   (test_warn_overall_when_scope_matrix_warnr   @  s0    	Anx''1q|nC4HHG
Qy\NOP   +!AiL>1M NO
	)!E(an!J-C S!G9n-7 
79%& '	*..~>?	A7
 +2244449'''>>Qr   c                F   | }|d   dz  dz  |d    dz  }|j                          t        |d   d|d    d       t        |d   |d   |d   |d	   
      }t        |      }|d   d   dk(  sJ |d   dk(  sJ |j                  dk(  sJ t        d |d   D              sJ y)u   task md 부재 시 scope_matrix FAIL (변경된 정책).

    Codex 결함 #2 — task md 부재 / 읽기 실패 / paths 빈 리스트는 차단(FAIL)이
    설계 의도다. 이전 구현은 WARN 으로 처리했으나 현 spec 2.7 에 따라 FAIL.
    r,   r   r   r-   r   rG   z] remove task mdrI   rH   rj   rm   rw   r   rk   r   c              3  $   K   | ]  }d |v  
 yw)rw   Nr    r!   rs     r   r#   z9test_fail_scope_matrix_task_md_missing.<locals>.<genexpr>r  s     Dq~"Dr   rp   N)r   rW   r`   rf   r~   r   r   s        r   &test_fail_scope_matrix_task_md_missingr   _  s     	Anx''1q|nC4HHGNN+!AiL>1A BC
	)!E(an!J-C S!G9n-7779'''>>QDGN,CDDDDr   c                   | }|d   dz  dz  |d    dz  }|j                   j                  dd       |j                  t        j                  d|d   i      d	       t        |d   d
|d    d       t        |d   |d   |d   |d         }t        |      }|d   d   dk(  sJ |d   dk(  sJ |j                  dk(  sJ y)uF   handoff JSON 부서진 schema → handoff_chain FAIL → overall FAIL.r,   r   handoffsr-   .jsonTr   r%   r&   rG   z] add handoff (invalid)rI   rH   rj   rm   ry   r   rk   r   N)	r(   r)   r+   rc   dumpsrW   r`   rf   r~   rh   r   handoffre   r   s        r   "test_warn_optional_handoff_invalidr   u  s    Anx'*4!I,u7MMGNN5tzz9a	l";<wO+!AiL>1H IJ
	)!E(an!J-C S!G9o.&8889'''>>Qr   c                X   | }|d   dz  dz  |d    dz  }|j                   j                  dd       |j                  dd	
       t        |d   d|d    d       t	        |d   |d   |d   |d         }t        |      }|d   d   dk(  sJ |d   dk(  sJ |j                  dk(  sJ y)uA   qc 보고서 PASS verdict → qc_report_guard=PASS, overall PASS.r,   r   reportsr-   r   Tr   z# QC Report

qc_verdict: PASS
r%   r&   rG   ] add qc reportrI   rH   rj   rm   r{   rl   rk   r   Nr(   r)   r+   rW   r`   rf   r~   rh   r   reportre   r   s        r   test_qc_report_pass_verdictr     s    A{^h&2)~S5IIF
MMt4
+g   +!AiL> AB
	)!E(an!J-C S!G9/0F:::9'''>>Qr   c                X   | }|d   dz  dz  |d    dz  }|j                   j                  dd       |j                  dd	
       t        |d   d|d    d       t	        |d   |d   |d   |d         }t        |      }|d   d   dk(  sJ |d   dk(  sJ |j                  dk(  sJ y)uA   qc 보고서 FAIL verdict → qc_report_guard=FAIL, overall FAIL.r,   r   r   r-   r   Tr   z# QC Report

qc_verdict: FAIL
r%   r&   rG   r   rI   rH   rj   rm   r{   r   rk   r   Nr   r   s        r   test_qc_report_fail_verdictr     s    A{^h&2)~S5IIF
MMt4
+g   +!AiL> AB
	)!E(an!J-C S!G9/0F:::9'''>>Qr   c                X   | }t        |d   |d   |d   |d   d      }|j                  dk(  s!J d|j                   d	|j                          t	        j
                  |j                        }|d
   dk(  sJ d|v sJ |d   dz  dz  |d   z  }|j                         sJ t        |j                  d            }t        |      dk\  sJ t	        j
                  |d   j                               }h d}t        |d   j                               |k(  sJ |d
   dv sJ d|v sJ d|v sJ y)uY   --json 미사용 시 evidence 파일이 .tasks/evidence/<id>/verify-*.json 에 저장됨.r-   rI   r,   rH   F)rI   r,   r   rX   r   zstdout=z
stderr=rk   rl   evidencer;   zverify-*.jsonr   >   r|   ru   rq   rr   rx   rw   rv   ry   rs   rt   r{   rm   )rl   r   r   verified_atro   N)r`   r~   rN   rb   rc   rd   existssortedgloblenrM   setkeys)rh   r   re   summaryev_direvsr   expected_keyss           r   test_evidence_file_writtenr     sS   A
	)eHK.jMC >>QK'#**Yszzl KKjj$G9'''   {^h&3a	lBF==??
_-
.Cs8q==jjR**,-GM wy!&&()]:::9!9999G###r   c                   | }|d   dz  j                  dd       t        |d   |d   |d   |d   	      }t        |      }|d
   dk(  sJ d|v sJ t        |d   t              sJ |d   sJ d       |d   d   |d   k(  sJ y)u   top-level ``fail_reasons`` 가 FAIL 사유 리스트를 노출 (Codex 결함 #5).

    spec 3.x: evidence JSON 최상위에 ``fail_reasons`` 필수.
    rH   r   r   r%   r&   r-   rI   r,   rj   rk   r   rp   u   fail_reasons 가 비어있음ro   N)r+   r`   rf   
isinstancer^   r   s       r   #test_top_level_fail_reasons_on_failr     s    
 	Az][ ,,UW,E
	)!E(an!J-C S!G9'''W$$$gn-t444>"C$CC"9n-1HHHHr   c           
        | }|d   dz  dz  |d    dz  }|j                   j                  dd       |j                  t        j                  dd	d
ddd      d       t        |d   d|d    d       t        |d   |d   |d   |d         }t        |      }|d   d   dk(  sJ t        d |d   D              sJ y)uQ   handoff JSON 의 task_id 가 인자 task_id 와 다르면 FAIL (Codex 결함 #3).r,   r   r   r-   r   Tr   h-1dev1dev5z	task-99992026-05-05T00:00:00Z
handoff_idfrom_botto_botr-   	timestampr%   r&   rG   z] add handoff (mismatch)rI   rH   rj   rm   ry   r   c              3  $   K   | ]  }d |v  
 yw)mismatchNr    r   s     r   r#   z5test_handoff_task_id_mismatch_fail.<locals>.<genexpr>  s     @1zQ@r   rp   N)	r(   r)   r+   rc   r   rW   r`   rf   r   r   s        r   "test_handoff_task_id_mismatch_failr     s    Anx'*4!I,u7MMGNN5

#" &3	
    +!AiL>1I JK
	)!E(an!J-C S!G9o.&888@(?@@@@r   c           
     Z   | }|d   dz  dz  |d    dz  }|j                   j                  dd       |j                  t        j                  dd	d
|d   dd      d       t        |d   d|d    d       t        |d   |d   |d   |d         }t        |      }|d   d   dk(  sJ y)uI   handoff JSON 의 from_bot 이 빈 문자열이면 FAIL (Codex 결함 #3).r,   r   r   r-   r   Tr   r    r   r   r   r%   r&   rG   z] add handoff (empty from_bot)rI   rH   rj   rm   ry   r   N)r(   r)   r+   rc   r   rW   r`   rf   r   s        r    test_handoff_from_bot_empty_failr     s    Anx'*4!I,u7MMGNN5

# Y<3	
    +!AiL>1O PQ
	)!E(an!J-C S!G9o.&888r   c                   | }|d   dz  dz  }|j                          t        |d   d|d    d       t        |d   |d   |d   |d   	      }t        |      }|d
   d   dk(  sJ |d   dk(  sJ |j                  dk(  sJ y)uN   detector 부재 시 mixed_commit FAIL (안전 측 차단 — Codex 결함 #4).r,   r   r   rG   r-   z] remove detectorrI   rH   rj   rm   rx   r   rk   r   N)r   rW   r`   rf   r~   )rh   r   detectorre   r   s        r   'test_mixed_commit_detector_missing_failr      s    A~	),FFHOO+!AiL>1B CD
	)!E(an!J-C S!G9n-7779'''>>Qr   c                    t        |       }|d   dz  j                  dd       t        |d   |d   |d   |d   	      }t        |      }|d
   d   dk(  sJ |d   dk(  sJ |j                  dk(  sJ y)u5   FAIL 1건 + 다른 PASS 들 → overall FAIL, exit 1.rH   z	extra.tmpr   r%   r&   r-   rI   r,   rj   rm   ru   r   rk   r   N)rT   r+   r`   rf   r~   )rP   r   re   r   s       r   $test_overall_priority_fail_over_warnr   /  s    !Az][ ,,UW,E
	)!E(an!J-C S!G9l+v5559'''>>Qr   )r   r   r   r   returnsubprocess.CompletedProcess)r,   r   r-   r   r.   z	list[str]r   r   )z	task-2459r   )rP   r   r-   r   rI   r   r   dict)r,   r   rV   r   r   None)r-   r   rI   z
str | Noner,   r   r   r   rX   boolrY   ztuple[str, ...]r   r   )re   r   r   r   )rP   r   ))__doc__
__future__r   rc   r   r[   pathlibr   pytest__file__resolver   	REPO_ROOTr]   rL   r   r1   rT   rW   r`   rf   fixturerh   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r    r   r   <module>r      sv   #   
   N""$,,Q/		Y	!4	49$'AA* UUU 
U 
	Up7   
 	
 
   !(" % %3H

L:4>E,"&,& RI*A492r   