
     jw                       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
ddlZddlmZ ddlZ ee      j!                         j"                  j"                  j"                  Z ee      ej(                  v r!ej(                  j+                   ee             ej(                  j-                  d ee             ddlmZmZmZmZmZmZmZmZm Z m!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z- d d!dZ.d"d#dZ/dd	gZ0d
dddZ1ddgg dZ2dg ddZ3ejh                  d$d       Z5	 	 	 	 	 d%	 	 	 	 	 d&dZ6d'dZ7d'dZ8d'dZ9d'dZ:d'dZ;d'dZ<d(dZ=d(dZ>d'dZ?d(dZ@d(dZAd)dZBy)*uI  task-2509+1 회귀 테스트 — review_gate_passed + fallback_review_passed 12 케이스.

QA 담당: 모리건(Morrigan)
대상: utils/merge_queue_executor.py (task-2509+1 보강)

케이스 목록 (회장 §1~12):
  TC-01  Gemini COMPLETED + unresolved 0 → review_gate_passed=true
  TC-02  Gemini UNAVAILABLE_QUOTA + fallback PASS → review_gate_passed=true (★ PR #58 fixture 재현)
  TC-03  Gemini UNAVAILABLE_QUOTA + fallback FAIL → BLOCK (FALLBACK_REVIEW_FAILED)
  TC-04  Gemini TIMEOUT + fallback PASS → AUTO_MERGE_ALLOWED
  TC-05  Gemini UNRESOLVED + real_bug → BLOCK (GEMINI_UNRESOLVED_BLOCK)
  TC-06  Gemini SCOPE_EXPANSION → CRITICAL_GEMINI_SCOPE_EXPANSION
  TC-07  smoke_command=None + dry_run=True → 정상 진행 (WARN/SKIP 허용)
  TC-08  smoke_command=None + dry_run=False → BLOCK (NON_DRY_RUN_REQUIRES_SMOKE_COMMAND)
  TC-09  HIGH_CORE risk file 변경 + Gemini quota → static_risky_pattern_scan 호출
  TC-10  후행 PR stale 재검증에서 BEHIND 감지
  TC-11  후행 PR effective diff 오염 감지
  TC-12  audit JSON에 신규 7 필드 기록 검증
    )annotationsN)Path)AUTO_MERGE_ALLOWEDBLOCKED_WITH_REASONCRITICAL_GEMINI_SCOPE_EXPANSIONFALLBACK_REVIEW_FAILEDGEMINI_COMPLETEDGEMINI_REAL_BUGGEMINI_SCOPE_EXPANSIONGEMINI_TIMEOUTGEMINI_UNAVAILABLE_QUOTAGEMINI_UNRESOLVEDGEMINI_UNRESOLVED_BLOCK"NON_DRY_RUN_REQUIRES_SMOKE_COMMANDRISK_LEVEL_HIGH_CORERISK_LEVEL_LOWExecutorContextQueueDecisionTaskSpecassess_risk_levelevaluate_prrecheck_following_prsstatic_risky_pattern_scanverify_head_lock_then_mergec                4    t        j                  g | ||      S )u   CompletedProcess 생성 헬퍼.)args
returncodestdoutstderr)
subprocessCompletedProcess)r   r   r   s      T/home/jay/workspace/tests/regression/test_merge_queue_executor_review_gate_2509p1.pycpr#   ?   s    &&B:f]cdd    c                *     g d fd	}|_         |S )u   args 패턴별 CompletedProcess 반환 fake runner.

    key = tuple of arg tokens to match (subset of args),
    value = CompletedProcess to return.
    Unmatched → returncode=0, empty stdout/stderr.
    c                     j                  t               ||d       xs i j                         D ]  \  }}t         fd|D              s|c S  t	               S )N)r   cwdtimeoutc              3  &   K   | ]  }|v  
 y wN ).0pr   s     r"   	<genexpr>z.make_runner.<locals>.runner.<locals>.<genexpr>P   s     .19.s   )appendlistitemsallr#   )r   r'   r(   pattern	completedcallsreturns_by_argss   `    r"   runnerzmake_runner.<locals>.runnerM   sY    d4jIJ#2#8b"?"?"A 	!GY.g..  	! tr$   )N<   )r5   )r6   r7   r5   s   ` @r"   make_runnerr9   D   s     E FLMr$   utils/merge_queue_executor.py@tests/regression/test_merge_queue_executor_review_gate_2509p1.pyCLEANzsha-clean-001main)mergeStateStatus
headRefOidbaseRefNameSUCCESS)statusdetailsrawok)rB   
unresolvedhookc                 D    t        dt        t              dg ddddd 	      S )Ntask-2509+1z'merge_queue/review_gate/fallback_reviewserial_only   TF)	task_idexpected_files	risk_area
dependencyparallel_policymerge_queue_positionstale_recheck_requiredcherry_pick_allowedsmoke_command)r   r0   EXPECTED_FILESr+   r$   r"   serial_task_specrV   i   s/    N+;%#!
 
r$   c                >    | t        i       } t        | ||d||      S )u%   기본 ExecutorContext 생성 헬퍼.T)r7   
pr_workdirrT   no_auditfixture_main_shamain_log_grep)r9   r   )r7   rX   rT   rZ   r[   s        r"   	_make_ctxr\   x   s1     ~R#)# r$   c           
        t               }dg}t        |      | _        t        d| dt        |      t        t
        t        |      }|j                  }|t        k(  }|s	t        j                  d|fd|t        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndd	z  }t        j                  d
|j                         dz   d|iz  }t!        t        j"                  |            dx}}|j$                  }d}||u }|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z   d|	iz  }
t!        t        j"                  |
            dx}x}}|j(                  }d}||u }|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z   d|	iz  }
t!        t        j"                  |
            dx}x}}|j,                  }|t.        k(  }|st        j                  d|fd|t.        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t.              rt        j                  t.              ndd	z  }t        j                  d|j,                   d|j&                         dz   d|iz  }t!        t        j"                  |            dx}}y)u   Gemini status=ok + unresolved 0 → gemini_status=GEMINI_COMPLETED,
    review_gate_passed=true, fallback_review_used=false, AUTO_MERGE_ALLOWED.r;   :   zsha-pr-tc01	pr_number	task_specpr_head_shaeffective_filesmerge_stateci_stategemini_statectx==z5%(py2)s
{%(py2)s = %(py0)s.gemini_status
} == %(py4)sdecisionr	   py0py2py4zexpected GEMINI_COMPLETED, got 
>assert %(py6)spy6NTisz:%(py2)s
{%(py2)s = %(py0)s.review_gate_passed
} is %(py5)srm   rn   py5z<review_gate_passed must be True for COMPLETED+unresolved=0: 
>assert %(py7)spy7Fz<%(py2)s
{%(py2)s = %(py0)s.fallback_review_used
} is %(py5)sz1fallback must NOT be used when Gemini COMPLETED: z0%(py2)s
{%(py2)s = %(py0)s.decision
} == %(py4)sr   !expected AUTO_MERGE_ALLOWED, got 
 / reason=)r\   r0   rM   r   CLEAN_MERGE_STATECI_OK	GEMINI_OKgemini_statusr	   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgAssertionError_format_explanationreview_gate_passedreasonfallback_review_usedfallback_check_detailsrk   r   )rV   rg   low_risk_filesrk   @py_assert1@py_assert3@py_format5@py_format7@py_assert4@py_format6@py_format8s              r"   -test_tc01_gemini_completed_review_gate_passesr      s    +C 	KN '+>&:#"!^,%	H !! !%55   !%5              "      &6    &6    *(*@*@)AB     && $ &$.  &$              '    +/    GxFWX     (( E (E1  (E              )    -2    <H<[<[;\]       22    2                    !3    !3    ,H,=,=+>jHYZ    r$   c           
     
   g d}t        |      }dg dddigd}dg}t        |      | _        t        d	| d
t        |      t        t
        ||      }|j                  }|t        k(  }|s	t        j                  d|fd|t        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }t        j                  d|j                         dz   d|iz  }	t        t        j                   |	            dx}}|j"                  }d}
||
u }|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      dz   d|iz  }t        t        j                   |            dx}x}}
|j$                  }d}
||
u }|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z   d|iz  }t        t        j                   |            dx}x}}
|j(                  }d}
||
u }|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z   d|iz  }t        t        j                   |            dx}x}}
|j,                  }|t.        k(  }|st        j                  d|fd|t.        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      d t        j                         v st        j                  t.              rt        j                  t.              nd dz  }t        j                  d!|j,                   d"|j*                         dz   d|iz  }	t        t        j                   |	            dx}}y)#u   ★ PR #58 fixture 재현 — Gemini quota daily limit 상황에서
    fallback_review 8조건 모두 PASS이면 review_gate_passed=true이고
    AUTO_MERGE_ALLOWED 결정을 받아야 한다.python3z-mpytestztests/smokerT   unavailable_quotaNmessageQuota exceeded for daily limitrB   rF   rG   errorsr;   r^   38334b09r_   rh   rj   rk   r   rl   'expected GEMINI_UNAVAILABLE_QUOTA, got rp   rq   Trr   ry   ru   z>fallback_review_used must be True for Gemini UNAVAILABLE_QUOTArw   rx   z>%(py2)s
{%(py2)s = %(py0)s.fallback_review_passed
} is %(py5)szfallback failed: rt   z!review_gate_passed must be True: rz   r   r{   r|   )r\   r0   rM   r   r}   r~   r   r   r   r   r   r   r   r   r   r   r   r   fallback_review_passedr   r   r   rk   r   )rV   	smoke_cmdrg   gemini_quotar   rk   r   r   r   r   r   r   r   s                r"   0test_tc02_gemini_unavailable_quota_fallback_passr      s   
 ;I
)
,C &?@A	L 	KN '+>&:#"^,%!	H !! !%==   !%=              "      &>    &>    2(2H2H1IJ     (( D (D0  (D              )    -1    	I     ** d *d2  *d              +    /3    H;;<=     && $ &$.  &$              '    +/    ,HOO+<=       22    2                    !3    !3    ,H,=,=+>jHYZ    r$   c           
     j   t        d      }dg dddigd}dg}t        |      | _        t        d| d	t        |      t        t
        ||
      }|j                  }|t        k(  }|s	t        j                  d|fd|t        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }t        j                  d|j                         dz   d|iz  }t        t        j                   |            dx}}|j"                  }d}	||	u }|st        j                  d|fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |	      dz  }
dd|
iz  }t        t        j                   |            dx}x}}	|j$                  }d}	||	u }|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z   d|
iz  }t        t        j                   |            dx}x}}	|j(                  }d}	||	u }|st        j                  d|fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |	      dz  }
dd|
iz  }t        t        j                   |            dx}x}}	|j*                  }|t,        k(  }|s	t        j                  d|fd|t,        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t,              rt        j                  t,              nddz  }t        j                  d |j*                         dz   d|iz  }t        t        j                   |            dx}}|j.                  }t0        |v }|s	t        j                  d!|fd"t0        |f      d#t        j                         v st        j                  t0              rt        j                  t0              nd#dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d$|j.                         dz   d|iz  }t        t        j                   |            dx}}y)%u   Gemini quota 미가용이지만 fallback 8조건 중 1건 이상 FAIL 시
    BLOCKED_WITH_REASON + reason에 FALLBACK_REVIEW_FAILED.Nr   r   r   r   r   r;   r^   zsha-pr-tc03r_   rh   rj   rk   r   rl   r   rp   rq   Trr   ry   ru   assert %(py7)srx   Fr   z,fallback must FAIL when smoke_command=None: rw   rt   rz   r   "expected BLOCKED_WITH_REASON, got in)z.%(py0)s in %(py4)s
{%(py4)s = %(py2)s.reason
}r   z,reason must mention FALLBACK_REVIEW_FAILED: )r\   r0   rM   r   r}   r~   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rk   r   r   r   )rV   rg   r   r   rk   r   r   r   r   r   r   r   s               r"   7test_tc03_gemini_unavailable_quota_fallback_fail_blocksr      s    $
'C &?@A	L 	KN '+>&:#"!^,%!	H !! !%==   !%=              "      &>    &>    2(2H2H1IJ     ((0D0(D0000(D00000080008000(000D0000000** e *e3  *e              +    /4    7x7V7V6WX     &&/%/&%////&%//////8///8///&///%///////  33    3                    !4    !4    -X->->,?@     &.__ !_4   !_      "    "      &.    &.    &5    7x6GH    r$   c           
     D
   g d}t        |      }dg dddigd}dg}t        |      | _        t        d	| d
t        |      t        t
        ||      }|j                  }|t        k(  }|s	t        j                  d|fd|t        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }t        j                  d|j                         dz   d|iz  }	t        t        j                   |	            dx}}|j"                  }d}
||
u }|st        j                  d|fd||
f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |
      dz  }dd|iz  }t        t        j                   |            dx}x}}
|j$                  }d}
||
u }|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z   d|iz  }t        t        j                   |            dx}x}}
|j(                  }d}
||
u }|st        j                  d|fd||
f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |
      dz  }dd|iz  }t        t        j                   |            dx}x}}
|j*                  }|t,        k(  }|st        j                  d|fd|t,        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t,              rt        j                  t,              nddz  }t        j                  d |j*                   d!|j.                         dz   d|iz  }	t        t        j                   |	            dx}}y)"uH   Gemini polling timeout이지만 fallback 8조건 모두 PASS → 통과.r   r   r(   Nr   zPolling deadline exceededr   r;   r^   zsha-pr-tc04r_   rh   rj   rk   r   rl   zexpected GEMINI_TIMEOUT, got rp   rq   Trr   ry   ru   r   rx   r   zfallback must PASS: rw   rt   rz   r   r{   r|   )r\   r0   rM   r   r}   r~   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rk   r   r   )rV   r   rg   gemini_timeoutr   rk   r   r   r   r   r   r   r   s                r"   &test_tc04_gemini_timeout_fallback_passr   !  s    :I
)
,C :;<	N 	KN '+>&:#"!^,%#	H !! !^3   !^              "      &4    &4    ((>(>'?@     ((0D0(D0000(D00000080008000(000D0000000** d *d2  *d              +    /3    x>>?@     &&.$.&$....&$......8...8...&...$.......  22    2                    !3    !3    ,H,=,=+>jHYZ    r$   c           
     b   t               }ddddgdddgddd}t        d| d	t        t              t        t
        ||
      }|j                  }t        t        f}||v }|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z   d|iz  }t!        t        j"                  |            dx}x}}|j$                  }|t&        k(  }|st        j                  d|fd|t&        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t&              rt        j                  t&              nddz  }	t        j                  d|j$                   d|j(                         dz   d|	iz  }
t!        t        j"                  |
            dx}}|j*                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t!        t        j"                  |            dx}x}}y)u   Gemini auto_triage_candidate (inside expected) → GEMINI_UNRESOLVED_BLOCK.
    real_bug=True 분류 시에도 머지 차단되어야 한다.auto_triage_candidater:   zreal bug: NPE on line 42pathbodyauto_gemini_triageT)rB   rF   insiderG   real_bugr^   zsha-pr-tc05r_   r   )z5%(py2)s
{%(py2)s = %(py0)s.gemini_status
} in %(py5)srk   ru   z3expected GEMINI_REAL_BUG or GEMINI_UNRESOLVED, got rw   rx   Nrh   rz   r   rl   z&expected GEMINI_UNRESOLVED_BLOCK, got r|   rp   rq   Frr   rt   r   )r\   r   r0   rU   r}   r~   r   r
   r   r   r   r   r   r   r   r   r   r   rk   r   r   r   )rV   rg   gemini_unresolvedrk   r   r   r   r   r   r   r   s              r"   +test_tc05_gemini_unresolved_real_bug_blocksr   M  s    +C *4>XY
 5>XY
 %
 "!^,%&	H !! o7H%I !%II  !%I              "    &J    >h>T>T=UV       77    7                    !8    !8    11B1B0C:hooM^_     &&/%/&%////&%//////8///8///&///%///////r$   c           
        t               }ddddgdddgdt        d}t        d| dt        t              t
        t        ||	      }|j                  }|t        k(  }|s	t        j                  d
|fd|t        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }t        j                  d|j                         dz   d|iz  }t!        t        j"                  |            dx}}|j$                  }|t&        k(  }|s	t        j                  d
|fd|t&        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t&              rt        j                  t&              nddz  }t        j                  d|j$                         dz   d|iz  }t!        t        j"                  |            dx}}|j(                  }|t        k(  }|s	t        j                  d
|fd|t        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }t        j                  d|j(                         dz   d|iz  }t!        t        j"                  |            dx}}|j*                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }	dd|	iz  }
t!        t        j"                  |
            dx}x}}y)u   Gemini critical_scope_expansion → BLOCKED_WITH_REASON +
    critical_code=CRITICAL_GEMINI_SCOPE_EXPANSION + classify→GEMINI_SCOPE_EXPANSION.critical_scope_expansionzutils/unrelated_file.pyz$fix scope expansion outside expectedr   critical_escalation_reporter)rB   rF   outsiderG   critical_coder^   zsha-pr-tc06r_   rh   rj   rk   r   rl   z%expected GEMINI_SCOPE_EXPANSION, got rp   rq   Nrz   r   r   )z5%(py2)s
{%(py2)s = %(py0)s.critical_code
} == %(py4)sr   z<expected critical_code=CRITICAL_GEMINI_SCOPE_EXPANSION, got Frr   rt   ru   r   rx   )r\   r   r   r0   rU   r}   r~   r   r   r   r   r   r   r   r   r   r   r   rk   r   r   r   )rV   rg   gemini_scoperk   r   r   r   r   r   r   r   s              r"   )test_tc06_gemini_scope_expansion_criticalr   w  s    +C -.8^_
 /8^_
 /8
L "!^,%!	H !! !%;;   !%;              "      &<    &<    00F0F/GH       33    3                    !4    !4    -X->->,?@     !! !%DD   !%D              "      &E    &E    GxG]G]F^_     &&/%/&%////&%//////8///8///&///%///////r$   c                    t        d      } t        t        ddd      }t        |d| d d	      }|j                  }|t        k(  }|st        j                  d
|fd|t        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }t        j                  d|j                   d|j                         dz   d|iz  }t        t        j                  |            dx}}g }|j                  }|}|sd}	|	}t        |v}|sgddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }|j!                  |       |s+ddt        j                  	      iz  }
|j!                  |
       t        j"                  |d      i z  }t        j                  d|fdt        |f      dt        j                         v st        j                  t              rt        j                  t              nd|dz  }t        j                  d|j                         dz   d|iz  }t        t        j                  |            dx}x}x}x}}	y) u   smoke_command=None + dry_run=True 시:
    verify_head_lock_then_merge가 BLOCK하지 않고 dry-run 결과 그대로 통과.
    회장 §5: dry_run=True + None → WARN/SKIP 허용.Nr   r^   rI   sha-locked-tc07rk   r`   rL   pr_head_sha_startc                     y)Nr   r+   _ns    r"   <lambda>z9test_tc07_dry_run_without_smoke_allowed.<locals>.<lambda>      r$   Trk   r`   rg   fetch_pr_head_at_mergedry_runrh   rz   resultr   rl   z6dry_run=True + smoke_command=None must NOT BLOCK, got r|   rp   rq    #%(py6)s
{%(py6)s = %(py4)s.reason
}ro   rq   %(py9)spy9   )not in)z%(py0)s not in (%(py12)s)r   rm   py12zEdry_run=True path must not raise NON_DRY_RUN_REQUIRES_SMOKE_COMMAND: 
>assert %(py14)spy14)r\   r   r   r   rk   r   r   r   r   r   r   r   r   r   r   r   r/   _format_booloprg   rk   r   r   r   r   r   @py_assert5@py_assert2@py_assert8@py_format10@py_format11@py_format13@py_format15s                 r"   'test_tc07_dry_run_without_smoke_allowedr     s    $
'C#+	H );F ?? ?00   ?0                    1    1    A@QQ[\b\i\i[jk    fmm m mr r - J      7=    7=    7D    7D   HJ      -      .    .     PPVP]P]_     r$   c                    t        d      } t        t        ddd      }t        |d| d d	      }|j                  }|t
        k(  }|s	t        j                  d
|fd|t
        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t
              rt        j                  t
              nddz  }t        j                  d|j                         dz   d|iz  }t        t        j                  |            dx}}g }|j                  }|}|sd}	|	}t         |v }|sgddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }|j#                  |       |s+ddt        j                  	      iz  }
|j#                  |
       t        j$                  |d      i z  }t        j                  d|fdt         |f      dt        j                         v st        j                  t               rt        j                  t               nd|dz  }t        j                  d|j                         dz   d|iz  }t        t        j                  |            dx}x}x}x}}	y)u   smoke_command=None + dry_run=False → BLOCK + NON_DRY_RUN_REQUIRES_SMOKE_COMMAND.
    회장 §5: 자동 머지를 실제 수행하려면 smoke_command 정의 필수.Nr   r^   rI   sha-locked-tc08r   c                     y)Nr   r+   r   s    r"   r   z<test_tc08_non_dry_run_without_smoke_blocks.<locals>.<lambda>  r   r$   Fr   rh   rz   r   r   rl   r   rp   rq   r   r   r   r   r   r   r   )z%(py0)s in (%(py12)s)r   r   z8reason must contain NON_DRY_RUN_REQUIRES_SMOKE_COMMAND: r   r   )r\   r   r   r   rk   r   r   r   r   r   r   r   r   r   r   r   r   r/   r   r   s                 r"   *test_tc08_non_dry_run_without_smoke_blocksr     sw    $
'C#+	H );F ?? ?11   ?1                    2    2    -V__,=>    &-- - -2 2 - F      39    39    3@    3@   DF      -      .    .     C6==/R     r$   c           
        g d}t        |      }dg}t        |      | _        dg dddigd}t        d	| d
t        |      t        t
        ||      }|j                  }|t        k(  }|s	t        j                  d|fd|t        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }t        j                  d|j                         dz   d|iz  }	t        t        j                   |	            dx}}|j"                  }d}
||
u }|st        j                  d|fd||
f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |
      dz  }dd|iz  }t        t        j                   |            dx}x}}
|j$                  xs i j'                  di       }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                  d|       d z   d!|iz  }t        t        j                   |            dx}}t)        |      }d"}||v }|st        j                  d|fd||f      t        j                  |      d#t        j                         v st        j                  |      rt        j                  |      nd#dz  }t        j                  d$|       d z   d!|iz  }t        t        j                   |            dx}}|j&                  }d%}
 ||
      }t+        |t              }|s`t        j                  d&|       d'z   d(t        j                         v st        j                  t*              rt        j                  t*              nd(d#t        j                         v st        j                  |      rt        j                  |      nd#t        j                  |      t        j                  |
      t        j                  |      d)t        j                         v st        j                  t              rt        j                  t              nd)t        j                  |      d*z  }t        t        j                   |            dx}x}
x}}t-        |      }|t        k(  }
|
s/t        j                  d|
fd+|t        f      d,t        j                         v st        j                  t,              rt        j                  t,              nd,d-t        j                         v st        j                  |      rt        j                  |      nd-t        j                  |      dt        j                         v st        j                  t              rt        j                  t              ndd.z  }dd|iz  }t        t        j                   |            dx}}
d/g}t-        |      }|t.        k(  }|st        j                  d|fd0|t.        f      d,t        j                         v st        j                  t,              rt        j                  t,              nd,t        j                  |      t        j                  |      d1t        j                         v st        j                  t.              rt        j                  t.              nd1d2z  }	d3d4|	iz  }t        t        j                   |            dx}x}}y)5u   utils/merge_queue_executor.py 변경 시 risk_level=HIGH_CORE.
    Gemini quota 미가용 fallback 시 static_risky_pattern_scan이 실행되고,
    fallback_check_details.checks.static_risky_scan_pass_if_high_core 항목 존재.r   r   r:   r   Nr   r   r   r^   zsha-pr-tc09r_   rh   )z2%(py2)s
{%(py2)s = %(py0)s.risk_level
} == %(py4)srk   r   rl   z#expected RISK_LEVEL_HIGH_CORE, got rp   rq   Trr   ry   ru   r   rx   checks#static_risky_scan_pass_if_high_corer   )z%(py1)s in %(py3)spy1py3zPfallback_check_details.checks must include static_risky_scan_pass_if_high_core: 
>assert %(py5)srv   passedscanz$scan result must have 'passed' key: 
violationszscan.violations must be list: zp
>assert %(py10)s
{%(py10)s = %(py0)s(%(py7)s
{%(py7)s = %(py3)s
{%(py3)s = %(py1)s.get
}(%(py5)s)
}, %(py8)s)
}
isinstancer0   )rm   r   r   rv   rx   py8py10)z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py5)sr   high_core_files)rm   r   r   rv   zdocs/readme.md)z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py6)sr   )rm   rn   ro   rq   assert %(py8)sr   )r\   r0   rM   r   r}   r~   
risk_levelr   r   r   r   r   r   r   r   r   r   r   r   getr   r   r   r   )rV   r   rg   r   r   rk   r   r   r   r   r   r   r   r   @py_assert0r   @py_format4r   @py_assert6@py_assert9r   r   @py_format9s                          r"   -test_tc09_high_core_static_risky_pattern_scanr     s    ;I
)
,C 77O&*?&;# &?@A	L "!_-%!	H  "66   "6                    #7    #7    .h.A.A-BC     ((0D0(D0000(D00000080008000(000D0000000--3882FF0 0F:  0F    1      5;    5;    [[aZbc    
 %_5DJ8tJJJ8tJJJ8JJJJJJtJJJtJJJJCD6JJJJJJJhh | h|, :,d3 3   )/                            ,    -      /3    /3    4      
 _-E-1EEEEE-1EEEEEEEEEEEEEEEE_EEE_EEE-EEEEEE1EEEE1EEEEEEEE./B/0B0NBBBB0NBBBBBBBBBBBB/BBB0BBBBBBNBBBNBBBBBBBr$   c                    t        j                  dddddigd      } t        dt        |       i      }d	dgd
g}t	        ||      }t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t
              rt        j                  t
              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|d   }	|	d   }
d	}|
|k(  }|slt        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}}|	d   }
d}|
|u }|st        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }t        j                  d|	       dz   d|iz  }t        t        j                  |            dx}
x}}|	d    }
d}|
|u }|st        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }t        j                  d!|	       dz   d|iz  }t        t        j                  |            dx}
x}}|	d"   }
d#}|
|u }|slt        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}}|	d$   }
d#}|
|u }|slt        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}}y)%uC   후행 PR이 BEHIND 상태일 때 needs_recheck=True + behind=True.BEHINDzsha-behind-100r=   r   
utils/x.pyr>   r?   r@   files)100r   d   )r`   rM   r   rh   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenstatesrm   r   r   rq   r   r   Nr   r`   z%(py1)s == %(py4)sr   ro   assert %(py6)srq   behindTrr   z%(py1)s is %(py4)szbehind must be True: rp   needs_recheckzneeds_recheck must be True: conflictFblockedjsondumpsr9   r#   r   r  r   r   r   r   r   r   r   r   r   )behind_payloadr7   queuer	  r   r   r   r   r   stater   r   r   s                r"   %test_tc10_recheck_following_pr_behindr    s   ZZ$&<()	! N "N+ F <.ABE"5&1Fv;!;!;!33vv;!1IE$$$$$$$$$$$$$$$$$$$?CdC?d"CCC?dCCC?CCCdCCC&;E7$CCCCCCCC!QTQ!T)QQQ!TQQQ!QQQTQQQ-I%+QQQQQQQQ%%%%%%%%%%%%%%%%%%%$u$u$$$$u$$$$$$u$$$$$$$r$   c                 &   t        j                  dddddiddigd      } t        dt        | 	      i      }d
dgdgdg}t	        ||      }t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t
              rt        j                  t
              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|d   }	|	d   }
d
}|
|k(  }|slt        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}}|	d   }
d}|
|u }|st        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }t        j                  d|	       d z   d|iz  }t        t        j                  |            dx}
x}}|	d!   }
d}|
|u }|st        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }t        j                  d"|	       d z   d|iz  }t        t        j                  |            dx}
x}}y)#u  후행 PR의 effective_files가 expected에서 벗어났을 때(또는 prior와 달라졌을 때)
    needs_recheck=True 또는 contamination 신호 감지.

    state machine이 신규 필드(effective_diff_drift / expected_files_maintained /
    forbidden_path_present)를 모두 포함하지 않아도 최소한 needs_recheck=True 또는
    DIRTY/conflict 신호로 재검증 필요를 표현해야 한다.DIRTYzsha-101r=   r   r  z
utils/y.pyr  )101r  e   )r`   rM   prior_effective_filesr   rh   r  r  r	  r
  r   r   Nr   r`   r  r  r  rq   r  Trr   r  u/   contamination 후행 PR은 needs_recheck=True: rp   r  u   DIRTY → conflict=True: r  )contaminated_payloadr7   r  r	  r   r   r   r   r   r  r   r   r   s                r"   )test_tc11_recheck_following_pr_diff_driftr   5  sU     ::#\"\"
	'  "01 F
 +n&2^	
E #5&1Fv;!;!;!33vv;!1IE$$$$$$$$$$$$$$$$$$$! T !T)  !T    "    &*    :%A     II$IIIIIIIIIIII(A%&IIIIIIIIr$   c                *   ddl m} | dz  }| dz  }|j                  dd       |j                  dd       |dz  }|j                  |d|       |j                  |d	|       |j                  |d
|       t	        t
        ddt        ddt        dt
        ddddid      }|j                  |dd      }d}||u}	|	st        j                  d|	fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }
t        j                  d      dz   d|
iz  }t!        t        j"                  |            dx}	}t%        |      }|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*                  |j-                  d            }g d}|D ]  }||v }	|	st        j                  d |	fd!||f      d"t        j                         v st        j                  |      rt        j                  |      nd"d#t        j                         v st        j                  |      rt        j                  |      nd#d$z  }t        j                  d%| d&t/        |j1                                      d'z   d(|iz  }t!        t        j"                  |            d}	 |d)   }|t        k(  }|st        j                  d*|fd+|t        f      t        j                  |      d,t        j                         v st        j                  t              rt        j                  t              nd,d-z  }
d.d|
iz  }t!        t        j"                  |            dx}}|d/   }d}||u }|slt        j                  d0|fd1||f      t        j                  |      t        j                  |      d2z  }d3d4|iz  }t!        t        j"                  |            dx}x}}|d5   }d}||u }|slt        j                  d0|fd1||f      t        j                  |      t        j                  |      d2z  }d3d4|iz  }t!        t        j"                  |            dx}x}}|d6   }|t        k(  }|st        j                  d*|fd+|t        f      t        j                  |      d7t        j                         v st        j                  t              rt        j                  t              nd7d-z  }
d.d|
iz  }t!        t        j"                  |            dx}}|d8   }d}||u }|slt        j                  d0|fd1||f      t        j                  |      t        j                  |      d2z  }d3d4|iz  }t!        t        j"                  |            dx}x}}|d9   }|t
        k(  }|st        j                  d*|fd+|t
        f      t        j                  |      d:t        j                         v st        j                  t
              rt        j                  t
              nd:d-z  }
d.d|
iz  }t!        t        j"                  |            dx}}|d;   }d}||u }|slt        j                  d0|fd1||f      t        j                  |      t        j                  |      d2z  }d3d4|iz  }t!        t        j"                  |            dx}x}}|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}	}|j-                  d      j3                         d>   }t)        j*                  |      }|D ]  }||v }	|	st        j                  d |	fd!||f      d"t        j                         v st        j                  |      rt        j                  |      nd"d?t        j                         v st        j                  |      rt        j                  |      nd?d$z  }t        j                  d@| d&t/        |j1                                      d'z   d(|iz  }t!        t        j"                  |            d}	 y)Auo  write_audit 출력 JSON에 신규 7 필드(gemini_status, fallback_review_used,
    fallback_review_passed, risk_level, review_gate_passed, final_decision,
    critical_escalation)가 포함돼야 한다.

    글로벌 audit 로그가 더럽혀지지 않도록 EVENTS_DIR/AUDIT_DIR/GLOBAL_AUDIT_LOG를
    monkeypatch로 tmp_path 하위 디렉토리로 격리한다.r   NeventsauditT)parentsexist_okzmerge-queue.jsonl
EVENTS_DIR	AUDIT_DIRGLOBAL_AUDIT_LOGr^   rI   effective_diff_equals_expected)r   r   )rk   r`   rL   r   r   r   r   r   final_decisioncritical_escalationr   F)rL   rY   )is not)z%(py0)s is not %(py3)s
audit_path)rm   r   z2write_audit must return a path when no_audit=Falser   rv   z per-task audit must be written: zC
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}per_task_pathrl   zutf-8)encoding)	r   r   r   r   r   r*  r+  r   static_scan_violationsr   )z%(py0)s in %(py2)sfielddata)rm   rn   z#audit JSON missing required field 'z': keys=z
>assert %(py4)sro   r   rh   )z%(py1)s == %(py3)sr   r   zassert %(py5)sr   rr   r  r  r  rq   r   r   r   r   r*  r   r+  z#global audit log must be appended: 
global_logglobal_dataz"global audit jsonl missing field ')utils.merge_queue_executormerge_queue_executormkdirsetattrr   r   r   r   write_auditr   r   r   r   r   r   r   r   r   r   existsr  loads	read_textr0   keys
splitlines)tmp_pathmonkeypatchmq
events_dir	audit_dirr3  rk   r-  r   r   r   r   r.  r   r   r2  required_fieldsr1  @py_format3r   r   	last_liner5  s                          r"   %test_tc12_audit_includes_7_new_fieldsrH  `  s    ,H$J7"ITD1OOD4O000JL*5K3.
;#.!#') *.;[]a:bcH -%PJ!W:T!WWW:TWWWWWW:WWW:WWWTWWW#WWWWWWW $MU!U!UU%Em_#UUUUUUU=UUU=UUUUUU!UUUUUU::m--w-?@DO ! 
} 	
 	
u 	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
   	
 	
 		  	
 	
  2%diikAR@ST	
 	
 	
 	
 	

  < $<<<<< $<<<< <<<<<<$<<<<$<<<<<<<<&'/4/'4////'4///'///4///////()1T1)T1111)T111)111T11111115!55555!5555555555!5555!55555555$%--%----%---%---------- !7!%77777!%7777!777777%7777%77777777%&.$.&$....&$...&...$....... RRRR"Ej\ RRRRRRR:RRR:RRRRRRRRRRRR$$g$6AACBGI**Y'K  
# 	
 	
u 	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
  $ 	
 	
 		 $ 	
 	
  1x[EUEUEW@X?YZ	
 	
 	
 	
 	

r$   )r   r   r   )r   intr   strr   rJ  returnzsubprocess.CompletedProcessr*   )r6   zdict | None)rK  r   )NNNzmain-sha-fixture-001N)rX   z
str | NonerZ   rJ  rK  r   )rV   r   rK  None)rK  rL  )r@  r   rA  zpytest.MonkeyPatchrK  rL  )C__doc__
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   r  r    syspathlibr   r   __file__resolveparent	WORKSPACErJ  r   removeinsertr6  r   r   r   r   r	   r
   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r#   r9   rU   r}   r~   r   fixturerV   r\   r   r   r   r   r   r   r   r   r   r  r   rH  r+   r$   r"   <module>r\     s{  ( #     
   N""$++2299	y>SXXHHOOC	N# 3y> "     8e
. $F  !  )R@2t<	   !2 	 .!P4t*`&X$0T%0VD>2Cp%4%JVJ
r$   