
    i5                       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  ed      Z	e	dz  dz  Z
e	dz  d	z  d
z  ZdZde dZdZddZ	 d	 	 	 	 	 	 	 ddZddZddZeeef	 	 	 	 	 ddZd dZd dZd dZd dZd dZd dZy)!u  tests/taskctl/test_takeover.py
taskctl.py takeover 서브커맨드 테스트 (task-2458 Phase 2-B)

테스트 케이스:
- test_takeover_handoff_missing         — handoff 없음 → exit 1
- test_takeover_schema_invalid          — handoff schema invalid → exit 1
- test_takeover_from_branch_missing     — --from 가 존재하지 않는 branch → exit 1
- test_takeover_head_sha_mismatch       — handoff.head_sha != from-branch HEAD → exit 1
- test_takeover_changed_paths_violation — changed_paths ⊄ allowed_paths → exit 1
- test_takeover_pass                    — 정상 경로 → exit 0 + worktree + evidence 생성

fixture 패턴 (PASS 케이스):
1. tmp_path/repo 내 git repo 초기화
2. 초기 commit → task 브랜치 생성 → 추가 commit
3. refs/remotes/origin/main 을 설정 (origin/main 시뮬레이션)
4. valid handoff JSON 작성 (head_sha = task 브랜치 HEAD)
5. WORKSPACE_ROOT=tmp_path/repo 로 taskctl takeover 실행
    )annotationsN)Pathz-/home/jay/workspace/.worktrees/task-2458-dev4scriptsz
taskctl.pymemoryspecshandoff-schema.jsonz	task-9020ztask/z-dev4dev5c                8    t         ddt        d| dgddgg i ddd	d
S )Nz1.0dev4abc1234tests/taskctl/test_takeover.pytests/taskctl/zscripts/takeover_requestz2026-05-05T12:00:00Zu   takeover 테스트 진행 중)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)TEST_TASK_IDTEST_TASK_BRANCH)r   s    N/home/jay/workspace/.worktrees/task-2487-A-dev2/tests/taskctl/test_takeover.py_make_valid_handoffr    *   s;    *:;*J7,,7     c                    t        t        j                        }t        |      |d<   |r|j	                  |       t        j                  dt        t              g| z   t        |      ddd|      S )u   taskctl.py 실행 헬퍼.WORKSPACE_ROOTpython3T<   )cwdcapture_outputtexttimeoutenv)dictosenvironstrupdate
subprocessrunTASKCTL_SCRIPT)argsworkspace_root	extra_envr*   s       r   _run_taskctlr6   @   sh     rzz
C/C

9>>	C'(4/ r!   c                   | dz  j                  dd       ddfd
} |dd        |dd	d
        |ddd       dz  j                  d        |dd        |ddd        |ddd        |ddt               dz  j                  d        |dd        |dddt         d        |dd      j                  j                         } |dd       |fS )uA  base_dir/repo 내에 임시 git repo를 설정하고 (repo_path, task_branch_head_sha) 반환.

    절차:
    1. git init --initial-branch=main
    2. 초기 commit (origin/main 역할)
    3. task 브랜치 checkout → 추가 commit
    4. git update-ref refs/remotes/origin/main main (origin/main 참조 생성)
    repoTparentsexist_ok)checkc                d    t        j                  dgt        |      z   t              dd|       S )NgitT)r&   r'   r(   r<   )r0   r1   listr.   )r<   r3   r8   s     r   _gitz_setup_git_repo.<locals>._git`   s2    ~~Gd4j D	
 	
r!   initz--initial-branch=mainconfigz
user.emailztest@test.comz	user.namezTest Botz	README.mdadd.commitz-mzinit: main basez
update-refzrefs/remotes/origin/mainHEADcheckoutz-bztask_file.txtz	task workzfeat: z work	rev-parsemain)mkdir
write_textr   r   stdoutstrip)base_dirr@   r   r8   s      @r   _setup_git_reporO   T   s     fDJJtdJ+ 
 	()<1;
+ 
K##F+4*+ 	16: 	T+,	O''446,u56 K(//557H 	V>r!   c                P   | dz  dz  }|j                  dd       |dz  }|j                         s#|j                  t        j	                                | dz  }|j                  dd       dD ]e  }t
        dz  |z  }||z  }|j                         s%|j                         r6|j                  |j	                                |j                  d       g | dz  d	z  }|j                  dd       |t         d
z  }	|	j                  t        j                  |dd      d       |	S )uJ   repo/memory/handoffs/<task_id>.json 기록 + 스키마/start_guard 복사.r   r   Tr9   r   r   )zstart_task_guard.pyzvalidate_handoff.pyi  handoffsz.jsonF   )ensure_asciiindentutf-8encoding)rJ   existswrite_bytesSCHEMA_PATH
read_bytesWORKTREEchmodr   rK   jsondumps)
r8   dataspec_dir
schema_dstscripts_dstsnamesrcdsthdirhpaths
             r   _write_handoff_in_repori      s   h(HNN4$N/11J{5578 "KdT2? "U*E!::<

OOCNN,-IIe (?Z'DJJtdJ+l^5))E	TZZ5CgVLr!   c                    d| d|d|gS )Ntakeoverz--fromz--bot )r   from_branchnew_bots      r   _takeover_argsro      s     ;IIr!   c                   t        |       \  }}|dz  dz  }|j                  dd       |dz  j                  t        j	                                t        t               |      }|j                  dk(  s!J d|j                   d	|j                          |j                  |j                  z   }d
|j                         v s>d|v s9d|j                         v s&d|v s!J d|j                   d|j                          yyyy)u;   handoff JSON이 존재하지 않으면 takeover → exit 1.r   r   Tr9   r   r4      u!   handoff 없는데 exit 0
stdout: 	
stderr: handoffu   없음	not foundFAILu    에러 메시지 없음
stderr: 	
stdout: N)rO   rJ   rY   rZ   r[   r6   ro   
returncoderL   stderrlower)tmp_pathr8   _ra   resultcombineds         r   test_takeover_handoff_missingr      s   h'GD!h(HNN4$N/%%22;3I3I3KL.*4@F! 
,V]]O:fmm_U! }}v}},HX^^%%x(..**X	T
 
+6==/FMM?ST	  +   	&r!   c                x   t        |       \  }}t        dd}t        ||       t        t	               |      }|j
                  dk(  s!J d|j                   d|j                          |j                  |j                  z   }d|j                         v s'd|v s"d	|j                         v sd
|v s
J d|        yyyy)uE   handoff JSON이 schema를 만족하지 않으면 takeover → exit 1.r   )r   r   rq   rr   u$   schema invalid인데 exit 0
stdout: rs   schemarv   invalidu   검증u,   스키마 에러 메시지 없음
combined: N)	rO   r   ri   r6   ro   rx   rL   ry   rz   )r{   r8   r|   invalid_datar}   r~   s         r   test_takeover_schema_invalidr      s    h'GD!  ,L
 4..*4@F! 
/jX! }}v}},HHNN$$X((x	B
 
7xjAB	    )  	%r!   c                   t        |       \  }}t        |      }t        ||       d}t        t	        |      |      }|j
                  dk(  s!J d|j                   d|j                          |j                  |j                  z   }d|j                         v s,d|v s'd	|v s"d
|v sd|j                         v s
J d|        yyyyy)u8   --from 에 존재하지 않는 branch 지정 → exit 1.ztask/task-9999-devX)rm   rq   rr   u0   존재하지 않는 branch인데 exit 0
stdout: rs   branchrH   rv   u   없ru   u0   branch 없음 에러 메시지 없음
combined: N	rO   r    ri   r6   ro   rx   rL   ry   rz   )r{   r8   r   r`   nonexistent_branchr}   r~   s          r   !test_takeover_from_branch_missingr      s    $X.ND(x(D4&.#56F ! 
;FMM?*U[UbUbTcd! }}v}},HHNN$$("XH(..**F 
;8*EF	+ +   # 	%r!   c                   t        |       \  }}t        d      }t        ||       t        t	               |      }|j
                  dk(  s$J d|j                   d|j                   d|        |j                  |j                  z   }d|v s:d|j                         v s'd	|v s"d
|v sd|j                         v s
J d|        yyyyy)u2   handoff.head_sha != --from branch HEAD → exit 1.0000000deadbeefrq   rr   u(   head_sha 불일치인데 exit 0
stdout: rs   z
real_head_sha=r   shau	   불일치rv   mismatchu0   SHA 불일치 에러 메시지 없음
combined: Nr   )r{   r8   real_head_shar`   r}   r~   s         r   test_takeover_head_sha_mismatchr      s    )(3D- 01D4&.*4@F! 
3FMM?*V]]O
=/	+! }}v}},HhHNN$$("X))F 
;8*EF	* *  # % 	r!   c                   t        |       \  }}t        |      }dg|d<   ddg|d<   t        ||       t        t	               |      }|j
                  dk(  s!J d|j                   d	|j                          |j                  |j                  z   }d
|j                         v s'd|v s"d|j                         v sd|v s
J d|        yyyy)u+   changed_paths ⊄ allowed_paths → exit 1.r   r   r   zscripts/dangerous.pyr   rq   rr   u*   changed_paths 위반인데 exit 0
stdout: rs   allowedu   초과	violationrv   u0   경로 위반 에러 메시지 없음
combined: Nr   )r{   r8   r   r`   r}   r~   s         r   %test_takeover_changed_paths_violationr     s    $X.ND(x(D-.D(D 4&.*4@F! 
5fmm_Jv}}o^! }}v}},HX^^%%x(..**X	F
 
;8*EF	  +   	&r!   c           	     ,   t        |       \  }}t        |      }t        ||       t        t	               |      }|j
                  dk(  s!J d|j                   d|j                          |j                  dz  t         dt         z  |dz  t         dt         z  |j                  t         dt         z  g}t        d |D              }t        j                  g dt        |      d	d	
      }t        |j                  v xs t        |j                  v }|s3|s1J d| d|j                   d|j                   d|j                          t        |dz  dz  t        z  dz        }	t        j                  |	      }
|
s$J d|	 d|j                   d|j                          t!        j"                  t%        |
d         j'                  d            }|j)                  d      t        k(  s+d|v s&dt        |      j+                         v s
J d|        yyy)u   정상 경로: exit 0 + 새 worktree 존재 + evidence JSON 존재.

    검증 항목:
    1. exit code == 0
    2. .worktrees/<task-id>-<new-bot>/ 디렉토리 존재 (worktree add 완료)
    3. .tasks/evidence/<task-id>/takeover-*.json 존재
    rq   r   u%   정상 takeover인데 exit 1
stdout: rs   z
.worktrees-c              3  <   K   | ]  }|j                           y wN)rX   ).0ps     r   	<genexpr>z%test_takeover_pass.<locals>.<genexpr>N  s     EEs   )r>   worktreer?   T)r&   r'   r(   u8   새 worktree가 생성되지 않음
candidates checked: z
git worktree list:
rw   z.tasksevidenceztakeover-*.jsonu4   evidence JSON이 생성되지 않음
검색 패턴: rU   rV   r   rk   u   evidence JSON 내용 이상: N)rO   r    ri   r6   ro   rx   rL   ry   parentr   NEW_BOTanyr0   r1   r.   globr^   loadsr   	read_textgetrz   )r{   r8   r   r`   r}   new_worktree_candidatesworktree_foundwt_listworktree_in_listevidence_patternevidence_filesev_datas               r   test_takeover_passr   1  s;    %X.ND(x(D4&F ! 
0z&--Y! 	l"~Qwi%@@|ay99ay11
 E-DEENnn#I	G '..0RLGNN4R- 67 8&~~. /==/FMM?	<- 4(?Z7,FIZZ[YY/0N *+ ,==/FMM?	<> jjnQ/0::G:LMG;;y!\1Y'5IZ[^_f[g[m[m[oMo 
'y1oMo5I1r!   )r   r.   returnr+   r   )r3   	list[str]r4   r   r5   zdict | Noner   zsubprocess.CompletedProcess)rN   r   r   ztuple[Path, str])r8   r   r`   r+   r   r   )r   r.   rm   r.   rn   r.   r   r   )r{   r   )__doc__
__future__r   r   r^   r,   r0   pathlibr   r\   r2   rZ   r   r   r   r    r6   rO   ri   ro   r   r   r   r   r   r   rl   r!   r   <module>r      s   $ #   	  
 ?@I%4!G+.CC <.. 

2 "
  !	(.b2 #/&6")J #JJ.7JT2B8F8F6F::r!   