
    9bj                    z    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
 G d d      Z G d d      Z G d d	      Zy)
uR  task-2457 Phase 2-A: pre-commit hook 테스트.

명세(루가 동시 작성 중인 hook 본체):
  차단 케이스 (exit 1, stderr "[BLOCKED] ..."):
    1. .tasks/locks/<task-id>.lock 부재 → "start_task_guard not passed"
    2. 현재 branch == main → "main direct commit prohibited"
    3. branch가 task 패턴 아님 (예: feature/foo) → "branch does not match task pattern"
    4. branch task-id != lock task-id → "branch/lock task-id mismatch"

  PASS 케이스 (exit 0):
    P1. lock 존재 + branch task-id 일치 + main 아님
    P2. TASKCTL_BYPASS=1 + TASKCTL_BYPASS_REASON 제공 → bypass evidence 기록 + PASS

  bypass evidence 검증 (.tasks/evidence/<task-id>/bypass-<ts>.json):
    - bypass=true / timestamp(ISO8601) / actor=$USER / reason=$TASKCTL_BYPASS_REASON
    - reason 누락 시 → 가드 자체 FAIL (exit 1)
    )annotationsNc                  $    e Zd Zd Zd Zd Zd Zy)TestPreCommitFailc                4    ||d        ||d      }|j                   }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j                  d|j                  d	|j                        d
z   d|iz  }	t        t        j                  |	            dx}x}}d}
|j                  }|
|v }|st        j                  d|fd|
|f      t        j                  |
      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}
x}}d}
|j                  }|
|v }|st        j                  d|fd|
|f      t        j                  |
      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}
x}}y)u#   1) lock 파일이 없으면 차단.task/task-2457-dev3
pre-commit   ==z2%(py2)s
{%(py2)s = %(py0)s.returncode
} == %(py5)sresultpy0py2py5u$   lock 부재인데 통과됨. stdout= stderr=
>assert %(py7)spy7N	[BLOCKED]inz.%(py1)s in %(py5)s
{%(py5)s = %(py3)s.stderr
}py1py3r   assert %(py7)szstart_task_guard not passed
returncode
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgstdoutstderrAssertionError_format_explanation)selfgit_repocheckout_branchrun_hookr   @py_assert1@py_assert4@py_assert3@py_format6@py_format8@py_assert0@py_assert2s               P/home/jay/workspace/.worktrees/task-2696-dev7/tests/git_hooks/test_pre_commit.pytest_lock_missing_blocksz*TestPreCommitFail.test_lock_missing_blocks   s   "78(L1   	
A 	
 A% 	
 	
 A 	
 	
	6	
 	
   	
 	
 		  	
 	
 		 ! 	
 	
 		 %& 	
 	
  36==2C8FMMK\]	
 	
 	
 	
 	
 +fmm+{m++++{m+++{++++++f+++f+++m+++++++,==,====,===,===================    c                "    ||d      }|j                   }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j                  d|j                  d|j                        d	z   d
|iz  }t        t        j                  |            dx}x}}d}	|j                  }|	|v }
|
st        j                  d|
fd|	|f      t        j                  |	      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }dd
|iz  }t        t        j                  |            dx}	x}
}d}	|j                  }|	|v }
|
st        j                  d|
fd|	|f      t        j                  |	      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }dd
|iz  }t        t        j                  |            dx}	x}
}y)u*   2) main 브랜치 직접 commit은 차단.r   r	   r
   r   r   r   u$   main commit인데 통과됨. stdout=r   r   r   Nr   r   r   r   r   zmain direct commit prohibitedr   )r*   r+   r-   r   r.   r/   r0   r1   r2   r3   r4   s              r5   test_main_branch_blocksz)TestPreCommitFail.test_main_branch_blocks(   s    (L1   	
A 	
 A% 	
 	
 A 	
 	
	6	
 	
   	
 	
 		  	
 	
 		 ! 	
 	
 		 %& 	
 	
  36==2C8FMMK\]	
 	
 	
 	
 	
 +fmm+{m++++{m+++{++++++f+++f+++m+++++++.?&--?.-????.-???.??????&???&???-???????r7   c                J    ||d        ||dd        ||d      }|j                   }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      t        j                  |      d	z  }	t        j                  d
|j                  d|j                        dz   d|	iz  }
t        t        j                  |
            dx}x}}d}|j                  }||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}d}|j                  }||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}y)u5   3) task 패턴이 아닌 branch (feature/foo) 차단.zfeature/foo	task-2457task_idbranchr   r	   r
   r   r   r   u(   task 패턴 아닌데 통과됨. stdout=r   r   r   Nr   r   r   r   r   z"branch does not match task patternr   )r*   r+   r,   	make_lockr-   r   r.   r/   r0   r1   r2   r3   r4   s                r5   test_non_task_branch_blocksz-TestPreCommitFail.test_non_task_branch_blocks2   s    	-0(KF(L1   	
A 	
 A% 	
 	
 A 	
 	
	6	
 	
   	
 	
 		  	
 	
 		 ! 	
 	
 		 %& 	
 	
  7v}}6GxPVP]P]O`a	
 	
 	
 	
 	
 +fmm+{m++++{m+++{++++++f+++f+++m+++++++3Dv}}D3}DDDD3}DDD3DDDDDDvDDDvDDD}DDDDDDDr7   c                    ||d        ||dd       |dz  dz  dz  }t        j                  |j                  d            }d	|d
<   |j                  t        j                  |dd      d        ||d      }|j
                  }d}	||	k(  }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |	      dz  }t        j                  d|j                  d|j                        dz   d|iz  }t        t        j                   |            dx}x}
}	d}|j                  }	||	v }|st        j                  d|fd||	f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      dz  }dd|iz  }t        t        j                   |            dx}x}}	d}|j                  }	||	v }|st        j                  d|fd||	f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      dz  }dd|iz  }t        t        j                   |            dx}x}}	y)u&  4) branch task-id != lock 내부 task_id → 차단.

        주의: hook은 lock 파일을 `.tasks/locks/<branch-task-id>.lock` 경로로 찾는다.
        진짜 mismatch 시나리오는 그 파일이 존재하면서 내부 task_id 필드가
        다른 값을 가진 경우다.
        ztask/task-1000-dev3z	task-1000r<   .taskslocksztask-1000.lockutf-8encodingz	task-2000r=   F   )ensure_asciiindentr   r	   r
   r   r   r   u.   branch/lock 불일치인데 통과됨. stdout=r   r   r   Nr   r   r   r   r   zbranch/lock task-id mismatch)jsonloads	read_text
write_textdumpsr   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   )r*   r+   r,   r?   r-   	lock_pathpayloadr   r.   r/   r0   r1   r2   r3   r4   s                  r5    test_branch_lock_mismatch_blocksz2TestPreCommitFail.test_branch_lock_mismatch_blocks@   sC    	"78 	(	
 x''14DD	**Y00'0BC(	TZZeANY`a(L1   	
A 	
 A% 	
 	
 A 	
 	
	6	
 	
   	
 	
 		  	
 	
 		 ! 	
 	
 		 %& 	
 	
  =V]]<MXV\VcVcUfg	
 	
 	
 	
 	
 +fmm+{m++++{m+++{++++++f+++f+++m+++++++->>->>>>->>>->>>>>>>>>>>>>>>>>>>r7   N)__name__
__module____qualname__r6   r9   r@   rQ    r7   r5   r   r      s    	>@E?r7   r   c                      e Zd Zd Zd Zy)TestPreCommitPassc                2   d} |||        ||d|        ||d      }|j                   }d}||k(  }	|	st        j                  d|	fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      t        j                  |      d	z  }
t        j                  d
|j                  d|j                        dz   d|
iz  }t        t        j                  |            dx}x}	}y)u?   P1) lock 존재 + branch task-id 일치 + main 아님 → PASS.r   r;   r<   r   r   r
   r   r   r   u)   정상 케이스인데 차단됨. stdout=r   r   r   Nr   )r*   r+   r,   r?   r-   r>   r   r.   r/   r0   r1   r2   s               r5   test_valid_lock_branch_passesz/TestPreCommitPass.test_valid_lock_branch_passesc   s    '&)(K?(L1   	
A 	
 A% 	
 	
 A 	
 	
	6	
 	
   	
 	
 		  	
 	
 		 ! 	
 	
 		 %& 	
 	
  87HQWQ^Q^Pab	
 	
 	
 	
 	
 	
r7   c                |   d} |||       ddd} ||d|      }|j                   }d}||k(  }	|	st        j                  d|	fd	||f      d
t        j                         v st        j
                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }
t        j                  d|j                  d|j                        dz   d|
iz  }t        t        j                  |            dx}x}	}|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                  d            }|st        j                  dt        |j                                      dz   ddt        j                         v st        j
                  |      rt        j                  |      ndiz  }t        t        j                  |            t!        j"                  |d   j%                  d            }|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(}	 ||	      }|st        j                  d)|       d*z   d#t        j                         v st        j
                  |      rt        j                  |      nd#t        j                  |      t        j                  |	      t        j                  |      d+z  }t        t        j                  |            dx}x}	}|d(   }g }d,}||v }|}|r5g }|j(                  }d-} ||      }|}|sd.}||v } | }| sd/}!|d0d }"|!|"v }#|#}|}|st        j                  d1|fd2||f      t        j                  |      d3t        j                         v st        j
                  |      rt        j                  |      nd3d4z  }
d5d|
iz  }|j+                  |       |rd6d3t        j                         v st        j
                  |      rt        j                  |      nd3t        j                        t        j                        t        j                        d7z  }$j+                  |$       |st        j                  d1 fd8|f      t        j                  |      d3t        j                         v st        j
                  |      rt        j                  |      nd3d9z  }%d:d;|%iz  }&|j+                  |&       | s_t        j                  d1#fd<!"f      t        j                  |!      t        j                  |"      d=z  }'d>d?|'iz  }(|j+                  |(       t        j,                  |d@      i z  })dAdB|)iz  }*|j+                  |*       t        j,                  |d      i z  }+t        j                  dC|      dDz   dE|+iz  },t        t        j                  |,            dx}x}x}x}x}x}x}x}x}x}x} x}!x}#}"|j&                  }dF}	 ||	      }t.        j0                  }|j&                  }-dG}. |-|.      }/||/k(  }|st        j                  d|fdH||/f      d#t        j                         v st        j
                  |      rt        j                  |      nd#t        j                  |      t        j                  |	      t        j                  |      dIt        j                         v st        j
                  t.              rt        j                  t.              ndIt        j                  |      t        j                  |-      t        j                  |.      t        j                  |/      dJ	z  }0t        j                  dK|j'                  dF       dLt.        j0                  j'                  dG             dMz   dN|0iz  }1t        t        j                  |1            dx}x}	x}x}x}x}-x}.}/|j&                  }dO}	 ||	      }d}||k(  }|st        j                  d|fdP||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                  dQ|j'                  dO            d&z   d'|iz  }t        t        j                  |            dx}x}	x}x}}y)Ru@   P2) TASKCTL_BYPASS=1 + reason 제공 → PASS + evidence 기록.r   1z"emergency hotfix for chairman demo)TASKCTL_BYPASSTASKCTL_BYPASS_REASONr   	env_extrar   r
   r   r   r   u   bypass 통과 못함. stdout=r   r   r   NrB   evidencer;   u/   evidence 디렉토리가 만들어지지 않음zC
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.is_dir
}()
}evidence_dir)r   r   py4zbypass-*.jsonu-   bypass-*.json evidence 파일이 없음. dir=z
>assert %(py0)sr   bypass_filesrD   rE   bypassT)is)zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} is %(py9)sdata)r   r   rb   py6py9u   bypass 필드 != true: z
>assert %(py11)spy11	timestampu   timestamp 누락: zG
>assert %(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
})r   r   rb   rg   TZ+-
   r   )z%(py3)s in %(py5)sts)r   r   z%(py7)szI%(py17)s
{%(py17)s = %(py13)s
{%(py13)s = %(py11)s.endswith
}(%(py15)s)
})ri   py13py15py17)z%(py20)s in %(py22)s)py20py22z%(py24)spy24)z%(py27)s in %(py30)s)py27py30z%(py32)spy32r	   z%(py35)spy35u%   timestamp 가 ISO8601 형식 아님: z
>assert %(py38)spy38actorUSER)z%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} == %(py16)s
{%(py16)s = %(py12)s
{%(py12)s = %(py10)s
{%(py10)s = %(py8)s.environ
}.get
}(%(py14)s)
}os)	r   r   rb   rg   py8py10py12py14py16u   actor 가 $USER 와 불일치: z vs z
>assert %(py18)spy18reason)zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} == %(py9)su   reason 불일치: )r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   is_dirlistglobiterdirrJ   rK   rL   getendswithappend_format_boolopr~   environ)2r*   r+   r,   r-   r>   envr   r.   r/   r0   r1   r2   ra   @py_format5rc   @py_format1rf   @py_assert5@py_assert8@py_assert7@py_format10@py_format12@py_format7rp   r4   r3   @py_assert10@py_assert12@py_assert14@py_assert16@py_assert9@py_assert19@py_assert21@py_assert26@py_assert29@py_assert28@py_format18@py_format23@py_format25@py_format31@py_format33@py_format34@py_format36@py_format37@py_format39@py_assert11@py_assert13@py_assert15@py_format17@py_format19s2                                                     r5   2test_bypass_with_reason_passes_and_writes_evidencezDTestPreCommitPass.test_bypass_with_reason_passes_and_writes_evidenceo   s    '&) "%I
 (LC@   	
A 	
 A% 	
 	
 A 	
 	
	6	
 	
   	
 	
 		  	
 	
 		 ! 	
 	
 		 %& 	
 	
  ,FMM+<HV]]DUV	
 	
 	
 	
 	

  (*Z7+E""W"$W$WW&WWWWWWW|WWW|WWW"WWW$WWWWWWL--o>?kkLTR^RfRfRhMiLjkkkkkkk|kkk|kkkkk zz,q/33W3EFxxKKx!KTK!T)KKK!TKKKKKKtKKKtKKKxKKKKKK!KKKTKKK-DTF+KKKKKKKKxxAAx$A$AA(:4&&AAAAAAAtAAAtAAAxAAAAAA$AAAAAA+	
s 	
sby 	
bkk 	
# 	
k#. 	
. 	
.# 	
#) 	
s 	
bg 	
sg~ 	
 	
 	
sb 	
 	
 		  	
 	
	6	
 	
   	
 	
 		  	
 	
 	
	6	
		
 	
	6	
 	
  ! 	
 	
 		 ! 	
 	
 		 * 	
 	
 		 +. 	
 	
 		 / 	
 	
	6	
	 / 	
 	
# 	
 	
 		 36 	
 	
	6	
 	
  :< 	
 	
 		 :< 	
 	
 	
	6	
		
 	
sg 	
 	
 		 @C 	
 	
 		 GN 	
 	
 	
	6	
		
 	
 	
 	
	6	
		
 	
 	
  4B6:	
 	
 	
 	
 	
 	
 	
 	
 xx 	
 	
x  	
BJJ 	
JNN 	
6 	
N6$: 	
 $:: 	
 	
 	
 $: 	
 	
	6	
 	
   	
 	
 		  	
 	
 		  	
 	
 		   	
 	
 		 ! 	
 	
	6	
 	
  %' 	
 	
 		 %' 	
 	
 		 %/ 	
 	
 		 %3 	
 	
 		 4: 	
 	
 		 %; 	
 	
  .dhhw.?-@RZZ^^TZE[D\]	
 	
 	
 	
 	
 	
 	
 xx 	
 	
x! 	
%I 	
!%II 	
 	
!%I 	
 	
	6	
 	
   	
 	
 		  	
 	
 		  	
 	
 		 ! 	
 	
 		 " 	
 	
 		 &J 	
 	
  !(!3 67	
 	
 	
 	
 	
 	
r7   N)rR   rS   rT   rY   r   rU   r7   r5   rW   rW   b   s    

$
r7   rW   c                      e Zd Zd Zy)TestPreCommitBypassEvidencec                
   d} |||       ddi}i |}t         j                  j                  dd        ||d|      }|j                  }d}	||	k(  }
|
st	        j
                  d	|
fd
||	f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |	      dz  }t	        j                  d|j                  d|j                        dz   d|iz  }t        t	        j                  |            dx}x}
}	d}|j                  }	||	v }|st	        j
                  d|fd||	f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |	      dz  }dd|iz  }t        t	        j                  |            dx}x}}	d}|j                  }	|	j                  } |       }||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |	      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}x}	x}}y)uJ   TASKCTL_BYPASS=1 만 있고 reason 없음 → 가드 자체 FAIL (exit 1).r   r\   r[   r]   Nr   r^   r	   r
   r   r   r   u-   reason 없는 bypass인데 통과됨. stdout=r   r   r   r   r   r   r   r   r   )z`%(py1)s in %(py9)s
{%(py9)s = %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = %(py3)s.stderr
}.lower
}()
})r   r   r   r   rh   zassert %(py11)sri   )r~   r   popr   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   lower)r*   r+   r,   r-   r>   r   env_fullr   r.   r/   r0   r1   r2   r3   r4   @py_assert6r   r   r   s                      r5   !test_bypass_without_reason_blocksz=TestPreCommitBypassEvidence.test_bypass_without_reason_blocks   s.    '&)%c7 	

.5(LHE   	
A 	
 A% 	
 	
 A 	
 	
	6	
 	
   	
 	
 		  	
 	
 		 ! 	
 	
 		 %& 	
 	
  <FMM;LHU[UbUbTef	
 	
 	
 	
 	
 +fmm+{m++++{m+++{++++++f+++f+++m+++++++06==0=..0.00x00000x0000x00000060006000=000.000000000000r7   N)rR   rS   rT   r   rU   r7   r5   r   r      s    1r7   r   )__doc__
__future__r   builtinsr!   _pytest.assertion.rewrite	assertionrewriter   rJ   r~   r   rW   r   rU   r7   r5   <module>r      s;   $ #    	@? @?L1
 1
n1 1r7   