
    Ki;                    "   U 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d      Z	e	dz  dz  Z
e	dz  d	z  d
z  ZdZde dZeddedddgddgg i ddddZded<   	 d&	 	 	 	 	 	 	 d'dZd(dZd)dZd)dZd)dZd)d Zd)d!Zd)d"Zd)d#Zd)d$Zd)d%Zy)*u  tests/handoff/test_validate_handoff.py
validate_handoff.py CLI 검증 테스트 (task-2458 Phase 2-B)

테스트 케이스:
- test_validate_missing_handoff      — handoff JSON 미존재 → exit 1
- test_validate_schema_invalid       — schema invalid (필수 필드 누락) → exit 1
- test_validate_task_id_mismatch     — JSON task_id != --task → exit 1
- test_validate_branch_mismatch      — JSON current_branch != --branch → exit 1
- test_validate_head_sha_mismatch    — JSON head_sha != git HEAD → exit 1
- test_validate_changed_paths_violation — changed_paths가 allowed_paths 외 → exit 1
- test_validate_pending_work_too_long   — pending_work 4001자 → exit 1
- test_validate_pass                 — 모든 조건 충족 → exit 0
    )annotationsN)Pathz-/home/jay/workspace/.worktrees/task-2458-dev4scriptszvalidate_handoff.pymemoryspecshandoff-schema.jsonz	task-9010ztask/z-dev4z1.0dev4abc1234def5678z&tests/handoff/test_validate_handoff.pytests/handoff/scripts/takeover_request2026-05-05T12:00:00Zu   잔여 작업 없음)task_idschema_versionprevious_botcurrent_branchbase_shahead_shachanged_pathsallowed_pathsforbidden_pathstest_resultshandoff_reason
created_atpending_workdictVALID_HANDOFFc                    t        t        j                        }t        |      |d<   |r|j	                  |       t        j                  dt        t              g| z   t        t              ddd|      S )u"   validate_handoff.py 실행 헬퍼.WORKSPACE_ROOTpython3T   )cwdcapture_outputtexttimeoutenv)	r   osenvironstrupdate
subprocessrunVALIDATE_SCRIPTWORKTREE)argsworkspace_root	extra_envr'   s       :/home/jay/workspace/tests/handoff/test_validate_handoff.py_run_validater4   :   sg     rzz
C/C

9>>	C()D0M     c                D   | dz  dz  }|j                  dd       | dz  dz  }|j                  dd       |dz  }|j                         s#|j                  t        j	                                || dz  }|j                  t        j                  |dd	
      d       |S )uB   tmp workspace 내에 handoff JSON 파일 기록 후 경로 반환.r   handoffsTparentsexist_okr   r   .jsonF   ensure_asciiindentutf-8encoding)mkdirexistswrite_bytesSCHEMA_PATH
read_bytes
write_textjsondumps)	workspacer   datahdirspec_dir
schema_dsthpaths          r3   _write_handoffrQ   N   s    x*,DJJtdJ+ 8#g-HNN4$N/11J{5578gYe$$E	TZZ5CgVLr5   c                   | dz  dz  }|j                  dd       |dz  j                  t        j                                t	        dt
        g|       }|j                  dk(  s!J d	|j                   d
|j                          d|j                  v s@d|j                  j                         v s#d|j                  v sJ d|j                          yyy)u/   handoff JSON이 존재하지 않을 때 exit 1.r   r   Tr8   r   --taskr1      u-   handoff 없는데 exit 0 (비정상)
stdout: 	
stderr: u   없음z	not foundFAILu    에러 메시지 없음
stderr: N)
rC   rE   rF   rG   r4   TEST_TASK_ID
returncodestdoutstderrlower)tmp_pathrN   results      r3   test_validate_missing_handoffr_   c   s     ("W,HNN4$N/%%22;3I3I3KLHl3HMF! 
8zRXR_R_Q`a! v}}$v}}7J7J7L(LPVZ`ZgZgPg 
+FMM?;gPg(L$r5   c                R   t         ddd}t        | t         |       t        dt         g|       }|j                  dk(  s!J d|j                   d|j
                          |j
                  |j                  z   }d	|v s,d
|j                         v sd|v sJ d|j
                          yyy)uP   필수 필드가 누락된 handoff JSON → schema validation 실패 → exit 1.	interruptr   )r   r   r   rS   rT   rU   u$   schema invalid인데 exit 0
stdout: rV   rW   schemaSchemau*   스키마 에러 메시지 없음
stderr: N)rX   rQ   r4   rY   rZ   r[   r\   )r]   invalid_datar^   combineds       r3   test_validate_schema_invalidrf   w   s      %,	L 8\<8Hl3HMF! 
/jX! }}v}},HXX^^-=!=XAU 
5fmm_EUAU!=r5   c                ^   t        t              }d|d<   d|d<   t        | t        |       t	        dt        g|       }|j
                  dk(  s!J d|j                   d	|j                          d|j                  v s2d
|j                  v s#d|j                  v sJ d|j                          yyy)u)   JSON task_id != --task 인수 → exit 1.z	task-9999r   ztask/task-9999-dev4r   rS   rT   rU   u'   task_id 불일치인데 exit 0
stdout: rV   	   불일치rW   u2   task_id 불일치 에러 메시지 없음
stderr: N)r   r   rQ   rX   r4   rY   rZ   r[   r]   rL   r^   s      r3   test_validate_task_id_mismatchrj      s    D!DO2D	8\40Hl3HMF! 
26==/FMM?[! %)ESYS`S`I` 
=fmm_M`I`)E%r5   c                t   t        t              }d|d<   t        | t        |       t	        dt        ddg|       }|j
                  dk(  s!J d|j                   d	|j                          d
|j                  j                         v s2d|j                  v s#d|j                  v sJ d|j                          yyy)u2   JSON current_branch != --branch 인수 → exit 1.ztask/task-9010-dev4r   rS   z--branchztask/task-9010-dev9rT   rU   u&   branch 불일치인데 exit 0
stdout: rV   branchrh   rW   u1   branch 불일치 에러 메시지 없음
stderr: N	r   r   rQ   rX   r4   rY   rZ   r[   r\   ri   s      r3   test_validate_branch_mismatchrn      s    D2D	8\40	<-BCF ! 
1&--
6==/Z! v}}**,,v}}0LPVZ`ZgZgPg 
<V]]OLgPg0L,r5   c                   | dz  }|j                          t        j                  g d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       t        j                  dddt
        gt        |      dd       |dz  j	                  d       t        j                  g d	t        |      dd       t        j                  g dt        |      dd       t        j                  g dt        |      dd      j                  j                         }t        t              }d|d<   t
        |d<   |dz  dz  }|j                  dd       |dz  j                  t        j                                |dz  dz  }|j                  dd       |t         dz  }|j	                  t        j                  |dd      d        t!        d!t        d"g|#      }|j"                  d$k(  s%J d%|j                   d&|j$                   d'| d(       d|j$                  v s@d)|j$                  j'                         v s#d*|j$                  v sJ d+|j$                          y,y,y,)-u   JSON head_sha가 실제 git rev-parse 결과와 불일치 → exit 1.

    임시 git repo를 tmp_path 내에 만들어 HEAD SHA를 제어한다.
    repo)gitinitz--initial-branch=mainT)r#   checkr$   )rq   configz
user.emailztest@test.com)rq   rt   z	user.nameTestzinit.txtrr   )rq   add.)rq   commit-mrr   rq   checkoutz-bz
change.txtchange)rq   rx   ry   r{   )rq   z	rev-parseHEAD)r#   r$   r%   0000000r   r   r   r   r8   r   r7   r;   Fr<   r=   r@   rA   rS   z--check-head-sharT   rU   u(   head_sha 불일치인데 exit 0
stdout: rV   z

real_sha=z, recorded=0000000sharW   u.   SHA 불일치 에러 메시지 없음
stderr: N)rC   r,   r-   r*   rH   TEST_BRANCHrZ   stripr   r   rE   rF   rG   rX   rI   rJ   r4   rY   r[   r\   )r]   rp   real_sharL   rN   rM   rP   r^   s           r3   test_validate_head_sha_mismatchr      s    fDJJLNN;TRV"&(NNCTZ^"&(NN9s4yPT"&(	J""6*NN&CITRVWNN2D	"&( NN	
D+.CITZ^ 
L$$X.NN&CITRVWNN4#d)4"&( ~~$#d)DtfUUW 
 D D(D	 h(HNN4$N/%%22;3I3I3KL(?Z'DJJtdJ+l^5))E	TZZ5CgV	<!34F ! 
3FMM?*V]]O
hZ1	3! &%6==3F3F3H*HFV\VcVcLc 
9&--IcLc*H&r5   c                   t        t              }dg|d<   ddg|d<   t        | t        |       t	        dt        g|       }|j
                  dk(  s!J d	|j                   d
|j                          d|j                  j                         v s2d|j                  v s#d|j                  v sJ d|j                          yyy)uC   changed_paths에 allowed_paths 범위 밖 파일 포함 → exit 1.r   r   ztests/handoff/test_foo.pyzscripts/dangerous_script.pyr   rS   rT   rU   u*   changed_paths 위반인데 exit 0
stdout: rV   allowed   초과rW   u.   경로 위반 에러 메시지 없음
stderr: Nrm   ri   s      r3   %test_validate_changed_paths_violationr      s    D-.D#%D 8\40Hl3HMF! 
5fmm_Jv}}o^! ++--V]]1JfX^XeXeNe 
9&--IeNe1J-r5   c                   t        t              }d|d<   | dz  dz  }|j                  dd       |dz  j                  t        j                                | dz  dz  }|j                  dd       |t         d	z  }|j                  t        j                  |d
      d       t        dt        g|       }|j                  dk(  s!J d|j                   d|j                          y)uR  pending_work가 4001자이면 validate에서 FAIL → exit 1.

    스키마(maxLength=4000) 또는 validate_handoff 자체 검사 중 하나가 잡아야 한다.
    스키마 검사는 JSON 파일 내용 기반이므로, validate_handoff.py 에서 검사하는 경우
    파일에 4001자가 직접 기록된 JSON을 제공한다.
      xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxr   r   r   Tr8   r   r7   r;   F)r>   r@   rA   rS   rT   rU   u*   pending_work 4001자인데 exit 0
stdout: rV   N)r   r   rC   rE   rF   rG   rX   rH   rI   rJ   r4   rY   rZ   r[   )r]   rL   rN   rM   rP   r^   s         r3   #test_validate_pending_work_too_longr     s     D &D ("W,HNN4$N/%%22;3I3I3KLh+DJJtdJ+l^5))E	TZZ59GLHl3HMF! 
5fmm_Jv}}o^!r5   c                   t        t              }t        | t        |       t	        dt        g|       }|j
                  dk(  s!J d|j                   d|j                          	 t        j                  |j                        }j                  d	      t        k(  sJ d
|j                  d	              y# t        j                  $ r/}t        j                  d| d|j                          Y d}~od}~ww xY w)u9   유효한 handoff JSON → exit 0, stdout에 JSON 출력.rS   rT   r   u'   유효한 handoff인데 exit 1
stdout: rV   u$   stdout이 유효한 JSON이 아님: z	
stdout: Nr   u"   출력 JSON의 task_id 불일치: )r   r   rQ   rX   r4   rY   rZ   r[   rI   loadsJSONDecodeErrorpytestfailget)r]   rL   r^   out_dataes        r3   test_validate_passr   .  s    D8\40Hl3HMF! 
26==/FMM?[!Y::fmm, <<	"l2 
,X\\)-D,EF2  Y:1#ZWXXYs   %B6 6C8	%C33C8c                   | dz  dz  }|j                  d       |t         dz  }|j                  dd       t        t              }g d	|d
<   dg|d<   t        | t        |       t        dt        g|       }|j                  dk(  s!J d|j                   d|j                          |j                  |j                  z   }d|j                         v sd|v sd|v s
J d|        yyy)u   handoff allowed_paths가 task 파일 allowed_resources를 초과(forge)하면 → exit 1.

    capability snapshot 교차검증: 봇이 handoff JSON에 allowed_paths를 임의로
    넓혀 적어 권한을 우회하는 시도를 차단한다.
    r   tasksT)r9   z.mdut   # task-9010 — test
```yaml
allowed_resources:
  paths:
    - "scripts/"
  forbidden_paths:
    - ".github/**"
```
r@   rA   )r   z.github/workflows/zsecrets/r   zscripts/x.pyr   rS   rT   rU   u   forgery인데 exit 0
stdout: rV   forgeryr   u   task 파일u*   forgery 에러 메시지 없음
combined: N)rC   rX   rH   r   r   rQ   r4   rY   rZ   r[   r\   )r]   task_dirtask_mdrL   r^   re   s         r3   )test_validate_capability_snapshot_forgeryr   D  s    ("W,HNN4N L>--G	    DJD+,D8\40Hl3HMF! 
(z&--Q! }}v}},HX^^%%xH$@ 
5XJ?	@	%$   	&r5   )N)r0   z	list[str]r1   r   r2   zdict | Nonereturnzsubprocess.CompletedProcess)rK   r   r   r*   rL   r   r   r   )r]   r   )__doc__
__future__r   rI   r(   r,   pathlibr   r   r/   r.   rF   rX   r   r   __annotations__r4   rQ   r_   rf   rj   rn   r   r   r   r   r    r5   r3   <module>r      s   #  	   
 ?@Y&)>>!G+.CC l^5) !>?&
3((*t 0 "
  !	(*(.(,9@,@,&@r5   