
     j9s                       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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+ d$d%dZ,d&dZ-dd	gZ.d
dddZ/ddgg dZ0dg ddZ1ejd                  d'd       Z3	 	 	 	 	 d(	 	 	 	 	 d)dZ4d*dZ5d*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!ZBd+d"ZCd+d#ZDy),u  task-2509 회귀 테스트 — merge_queue_executor 12 케이스 + 보너스 2.

QA 담당: 모리건(Morrigan)
대상: utils/merge_queue_executor.py

케이스 목록:
  TC-01  queue head 아닌 경우 → WAITING_FOR_PREDECESSOR
  TC-02  queue head + 10조건 PASS → AUTO_MERGE_ALLOWED
  TC-03  BEHIND 상태 → merge sync (rebase X)
  TC-04  HEAD SHA 변경 → HEAD_SHA_LOCK_BROKEN
  TC-05  effective diff 오염 → DIFF_CONTAMINATION_REPLACEMENT + replacement_pr_runner
  TC-06  forbidden path → BLOCKED_WITH_REASON + CRITICAL_FORBIDDEN_PATH
  TC-07  CI failure → CI_FAILURE_BLOCK
  TC-08a Gemini auto_triage_candidate → GEMINI_UNRESOLVED_BLOCK
  TC-08b Gemini critical_scope_expansion → BLOCKED_WITH_REASON + CRITICAL_GEMINI_SCOPE_EXPANSION
  TC-09a mergeStateStatus=DIRTY → MERGE_STATE_NOT_CLEAN
  TC-09b mergeStateStatus=BLOCKED → BLOCKED_WITH_REASON + CRITICAL_BLOCK_OVERRIDE
  TC-10  post-merge smoke FAIL → BLOCKED_WITH_REASON + CRITICAL_POST_MERGE_SMOKE
  TC-11  post-merge smoke PASS → AUTO_MERGE_SUCCESS
  TC-12  후행 PR stale 재검증 state machine
  BONUS-1  assert_no_forbidden_git_flags: --admin → RuntimeError
  BONUS-2  assert_no_forbidden_git_flags: rebase → RuntimeError
    )annotationsN)Path)AUTO_MERGE_ALLOWEDAUTO_MERGE_SUCCESSBLOCKED_WITH_REASONCI_FAILURE_BLOCKCRITICAL_BLOCK_OVERRIDECRITICAL_FORBIDDEN_PATHCRITICAL_GEMINI_SCOPE_EXPANSIONCRITICAL_POST_MERGE_SMOKEDIFF_CONTAMINATION_REPLACEMENTGEMINI_UNRESOLVED_BLOCKHEAD_SHA_LOCK_BROKENMERGE_STATE_NOT_CLEANREPLACEMENT_PR_RUNNER_HOOKWAITING_FOR_PREDECESSORExecutorContextQueueDecisionTaskSpecassert_no_forbidden_git_flagsevaluate_prrecheck_following_prsverify_head_lock_then_mergec                4    t        j                  g | ||      S )u   CompletedProcess 생성 헬퍼.)args
returncodestdoutstderr)
subprocessCompletedProcess)r   r   r   s      F/home/jay/workspace/tests/regression/test_merge_queue_executor_2509.pycpr"   A   s    &&B:f]cdd    c                *     g d fd	}|_         |S )u   args 패턴별 CompletedProcess 반환 fake runner.

    key = frozenset of arg tokens to match,
    value = CompletedProcess to return.
    Unmatched → returncode=0, empty stdout/stderr.
    c                     j                  t               ||d       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>R   s     .19.s   )appendlistitemsallr"   )r   r&   r'   pattern	completedcallsreturns_by_argss   `    r!   runnerzmake_runner.<locals>.runnerO   sT    d4jIJ"1"7"7"9 	!GY.g..  	! tr#   )N<   )r3   )r4   r5   r3   s   ` @r!   make_runnerr7   F   s     E FLMr#   utils/merge_queue_executor.pyz2tests/regression/test_merge_queue_executor_2509.pyCLEANzsha-clean-001mainmergeStateStatus
headRefOidbaseRefNameSUCCESSstatusdetailsrawok)rA   
unresolvedhookc                 D    t        dt        t              dg ddddd 	      S )N	task-2509z(merge_queue/auto_merge/conflict_recoveryserial_only   TF)	task_idexpected_files	risk_area
dependencyparallel_policymerge_queue_positionstale_recheck_requiredcherry_pick_allowedsmoke_command)r   r.   EXPECTED_FILESr)   r#   r!   serial_task_specrU   k   s/    N+<%#!
 
r#   c                >    | t        i       } t        | ||d||      S )u%   기본 ExecutorContext 생성 헬퍼.T)r5   
pr_workdirrS   no_auditfixture_main_shamain_log_grep)r7   r   )r5   rW   rS   rY   rZ   s        r!   	_make_ctxr[   z   s1     ~R#)# r#   c           
        | }dg|_         dd}t        |      }t        d|dt        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}}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  }t        j                   d|j&                         dz   d|iz  }t#        t        j$                  |            dx}	x}}
y)u   dependency=["task-9999.merged"]이고 main_log_grep이 False 반환하면
    evaluate_pr이 WAITING_FOR_PREDECESSOR를 결정해야 한다.ztask-9999.mergedc                     y)NFr)   )_task_ids    r!   _not_mergedz6test_tc01_waiting_for_predecessor.<locals>._not_merged   s    r#   )rZ   c   
sha-pr-001	pr_number	task_specpr_head_shaeffective_filesmerge_stateci_stategemini_statectx==z0%(py2)s
{%(py2)s = %(py0)s.decision
} == %(py4)sdecisionr   py0py2py4z&expected WAITING_FOR_PREDECESSOR, got 
>assert %(py6)spy6Nz	task-9999inz.%(py1)s in %(py5)s
{%(py5)s = %(py3)s.reason
}py1py3py5zreason must mention task-9999: 
>assert %(py7)spy7)r^   strreturnbool)rN   r[   r   r.   rT   CLEAN_MERGE_STATECI_OK	GEMINI_OKrn   r   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgAssertionError_format_explanationreason)rU   specr_   rj   rn   @py_assert1@py_assert3@py_format5@py_format7@py_assert0@py_assert4@py_assert2@py_format6@py_format8s                 r!   !test_tc01_waiting_for_predecessorr      s    D)*DO +
.C ^,%	H   77    7                    !8    !8    11B1B0CD     ^(//^;/)^^^;/^^^;^^^^^^(^^^(^^^/^^^-LX__L]+^^^^^^^^r#   c           
        t               }t        d| dt        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
|j                          dz   d|iz  }t#        t        j$                  |            dx}}y)uR   모든 조건 정상 → evaluate_pr이 AUTO_MERGE_ALLOWED를 반환해야 한다.9   ra   rb   rk   rm   rn   r   ro   z!expected AUTO_MERGE_ALLOWED, got 
 / reason=rs   rt   N)r[   r   r.   rT   r   r   r   rn   r   r   r   r   r   r   r   r   r   r   r   )rU   rj   rn   r   r   r   r   s          r!   +test_tc02_all_gates_pass_auto_merge_allowedr      s&   
+C" ^,%	H   22    2                    !3    !3    ,H,=,=+>jHYZ    r#   c           
     b   d}t        dt        dd      i      }t        ||      }ddd	d
}t        d| dt	        t
              |t        t        |       |j                  D cg c]  }d|d   v sd|d   v s| }}|s{t        j                  d      dz   ddt        j                         v st        j                  |      rt        j                  |      ndiz  }t        t        j                   |            |d   d   }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}	}
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 cg c]  }d|d   v s| }}| }|s~t        j                  d |       d!z   dd"t        j                         v st        j                  |      rt        j                  |      nd"iz  }t        t        j                   |            d}yc c}w c c}w )#u   mergeStateStatus=BEHIND + pr_workdir 설정 시
    runner가 ['git','merge','origin/main','--no-edit']로 호출돼야 하고
    'rebase' 명령은 호출되지 않아야 한다.z/tmp/fake-pr-workdir)gitmergeorigin/mainr   zMerge made by ort.r   r   )r5   rW   BEHINDzsha-pr-behindr:   r;   r   rb   r   r   r   z5git merge origin/main --no-edit must have been calledz
>assert %(py0)srp   merge_callsr   ru   )z%(py1)s in %(py3)s
merge_args)ry   rz   z&expected 'origin/main' in merge args: z
>assert %(py5)sr{   Nz	--no-editz$expected '--no-edit' in merge args: rebasez$rebase must NOT be called, but got: z
>assert not %(py0)srebase_calls)r7   r"   r[   r   r.   rT   r   r   r3   r   r   r   r   r   r   r   r   r   )rU   rW   r5   rj   behind_merge_statecr   @py_format1r   r   r   @py_format4r   r   r   @py_format2s                   r!   ,test_tc03_behind_state_uses_merge_not_rebaser      s>    (J'qAU)V F 6j
9C %% "#^,&	 %llZg6.BuPQRXPYGY1ZKZOOOOOOOOO;OOO;OOOOOQ'J]=J&]]]=J]]]=]]]]]]J]]]J]]]]*PQ[P\(]]]]]]]Y;*$YYY;*YYY;YYYYYY*YYY*YYYY(LZL&YYYYYYY  &||E!x1V9/DAELERRRCL>RRRRRRR|RRR|RRRRRR [ Fs   $L'1L'9L'L,L,c                X   t               }t        t        ddd      }dd}t        |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}}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  }t        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  }t        j                  d|j                         dz   d|iz  }t        t        j                  |            dx}	x}}
y)u   pr_head_sha_start와 fetch_pr_head_at_merge가 다른 값 반환 시
    verify_head_lock_then_merge가 HEAD_SHA_LOCK_BROKEN을 반환해야 한다.r   rH   zsha-start-111rn   rc   rK   pr_head_sha_startc                     y)Nsha-changed-999r)   
_pr_numbers    r!   _changed_shaz<test_tc04_head_sha_changed_breaks_lock.<locals>._changed_sha       r#   Trn   rc   rj   fetch_pr_head_at_mergedry_runrk   rm   resultr   ro   z#expected HEAD_SHA_LOCK_BROKEN, got rs   rt   Nru   rw   rx   zreason must contain start sha: r|   r}   r   zreason must contain new sha: r   intr   r~   )r[   r   r   r   rn   r   r   r   r   r   r   r   r   r   r   r   )rU   rj   rn   r   r   r   r   r   r   r   r   r   r   r   s                 r!   &test_tc04_head_sha_changed_breaks_lockr      s5    +C #)	H! )+F ?? ?22   ?2                    3    3    .foo->?     ^fmm^?m+^^^?m^^^?^^^^^^f^^^f^^^m^^^/Nv}}o-^^^^^^^^^^-^^^^^^^^^^^^^^^^^^^^^1Nv}}o/^^^^^^^^r#   c           
        t               }t        t              dgz   }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$                  }t&        |v }|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	z  }t        j                  dt&         d|j$                         dz   d|iz  }t!        t        j"                  |            dx}}y)u   effective_files에 expected에 없는 추가 파일 → DIFF_CONTAMINATION_REPLACEMENT
    + reason에 'replacement_pr_runner' 포함.zutils/rogue_extra_file.pyr   ra   rb   rk   rm   rn   r   ro   z-expected DIFF_CONTAMINATION_REPLACEMENT, got rs   rt   Nru   )z.%(py0)s in %(py4)s
{%(py4)s = %(py2)s.reason
}r   zreason must mention 'z': )r[   r.   rT   r   r   r   r   rn   r   r   r   r   r   r   r   r   r   r   r   r   )rU   rj   contaminated_filesrn   r   r   r   r   s           r!   (test_tc05_diff_contamination_replacementr     s    +Cn-1L0MM" *%	H   >>    >                    !?    !?    88I8I7JK     *2 %8   %      &    &      *2    *2    *9      :;3x>OP    r#   c           
        t               }t        t              dgz   }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$                  }|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}}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  }t        j                  d|j(                         dz   d|iz  }t!        t        j"                  |            dx}x}
}	y)uy   effective_files에 '.github/workflows/ci.yml' 포함 →
    BLOCKED_WITH_REASON + critical_code=CRITICAL_FORBIDDEN_PATH.z.github/workflows/ci.ymlr   ra   rb   rk   rm   rn   r   ro   "expected BLOCKED_WITH_REASON, got rs   rt   Nz5%(py2)s
{%(py2)s = %(py0)s.critical_code
} == %(py4)sr
   z4expected critical_code=CRITICAL_FORBIDDEN_PATH, got ru   )z7%(py1)s in %(py5)s
{%(py5)s = %(py3)s.forbidden_paths
}rx   z/forbidden_paths must contain the invaded file: r|   r}   )r[   r.   rT   r   r   r   r   rn   r   r   r   r   r   r   r   r   r   r   critical_coder
   forbidden_paths)rU   rj   files_with_forbiddenrn   r   r   r   r   r   r   r   r   r   s                r!   !test_tc06_forbidden_path_invasionr   3  s    +C/3M2NN" ,%	H   33    3                    !4    !4    -X->->,?@     !! !%<<   !%<              "      &=    &=    ?x?U?U>VW     & )A)A %)AA  %)A    &      *2    *2    *B    :(:R:R9ST     r#   c           
        t               }t        dgg 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}}y)uP   ci_state.status=CI_FAILURE_BLOCK 시 결정이 CI_FAILURE_BLOCK이어야 한다.FAILUREr@   r   ra   rb   rk   rm   rn   r   ro   zexpected CI_FAILURE_BLOCK, got rs   rt   N)r[   r   r   r.   rT   r   r   rn   r   r   r   r   r   r   r   r   r   )rU   rj   ci_failrn   r   r   r   r   s           r!   test_tc07_ci_failure_blockr   S  s)   
+C)yk"MG" ^,%	H   00    0                    !1    !1    *(*;*;)<=    r#   c           
        t               }ddddgdddgd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}}y)uF   gemini_state.status=auto_triage_candidate → GEMINI_UNRESOLVED_BLOCK.auto_triage_candidater8   z	style nitpathbodyauto_gemini_triage)rA   rE   insiderF   r   ra   rb   rk   rm   rn   r   ro   z&expected GEMINI_UNRESOLVED_BLOCK, got rs   rt   N)r[   r   r.   rT   r   r   rn   r   r   r   r   r   r   r   r   r   r   )rU   rj   gemini_autorn   r   r   r   r   s           r!   'test_tc08a_gemini_auto_triage_candidater   m  sB   
+C * ?UV;[QR$	K " ^,% 	H   77    7                    !8    !8    11B1B0CD    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}}y)uy   gemini_state.status=critical_scope_expansion →
    BLOCKED_WITH_REASON + critical_code=CRITICAL_GEMINI_SCOPE_EXPANSION.critical_scope_expansionzsome/external/file.pyzreal bug outside expectedr   critical_escalation_reporter)rA   rE   outsiderF   r   r   ra   rb   rk   rm   rn   r   ro   r   rs   rt   Nr   r   z<expected critical_code=CRITICAL_GEMINI_SCOPE_EXPANSION, got )r[   r   r   r.   rT   r   r   rn   r   r   r   r   r   r   r   r   r   r   r   )rU   rj   gemini_criticalrn   r   r   r   r   s           r!   *test_tc08b_gemini_critical_scope_expansionr     s7    +C - 7A\]^4>YZ[.8O " ^,%$	H   33    3                    !4    !4    -X->->,?@     !! !%DD   !%D              "      &E    &E    GxG]G]F^_    r#   c           
     \   t               }d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}}y)uL   mergeStateStatus=DIRTY → MERGE_STATE_NOT_CLEAN 또는 BLOCKED_WITH_REASON.DIRTYra   r:   r;   r   rb   ru   )z0%(py2)s
{%(py2)s = %(py0)s.decision
} in %(py5)srn   rp   rq   r{   z;expected MERGE_STATE_NOT_CLEAN or BLOCKED_WITH_REASON, got r|   r}   N)r[   r   r.   rT   r   r   rn   r   r   r   r   r   r   r   r   r   r   r   )	rU   rj   dirty_statern   r   r   r   r   r   s	            r!   test_tc09a_merge_state_dirtyr     s   
+C $"K " ^,	H  !68K L  LL   L                  !M    FhFWFWEXY     r#   c           
        t               }ddd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}}y)uY   mergeStateStatus=BLOCKED → BLOCKED_WITH_REASON + critical_code=CRITICAL_BLOCK_OVERRIDE.BLOCKEDra   r:   r;   r   rb   rk   rm   rn   r   ro   r   rs   rt   Nr   r	   z4expected critical_code=CRITICAL_BLOCK_OVERRIDE, got )r[   r   r.   rT   r   r   rn   r   r   r   r   r   r   r   r   r   r   r   r	   )rU   rj   blocked_statern   r   r   r   r   s           r!   test_tc09b_merge_state_blockedr     s   
+C &"M " ^,!	H   33    3                    !4    !4    -X->->,?@     !! !%<<   !%<              "      &=    &=    ?x?U?U>VW    r#   c           	     N   g d}|| _         t        dt        dd      t        |      t        dd      i      }t	        |d	|d
d      }t        t        ddd      }d%d}t        |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}}|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}||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#z   d$|iz  }t#        t        j$                  |            d	x}x}}y	)&u   squash merge 성공(returncode=0) 후 smoke 실패(returncode=1) →
    BLOCKED_WITH_REASON + critical_code=CRITICAL_POST_MERGE_SMOKE.python3z-mpytestztests/smokeghprr   57z--squashr   	PR mergedr      zSMOKE FAILED: 3 tests failed)r   r   NTmain-sha-fixture-001r5   rW   rS   rX   rY   r   rH   sha-pr-smoke-001r   c                     y)Nr   r)   r   s    r!   	_same_shaz5test_tc10_post_merge_smoke_failure.<locals>._same_sha  s    !r#   Fr   rk   rm   r   r   ro   z/expected BLOCKED_WITH_REASON (smoke fail), got rs   rt   r   r   z6expected critical_code=CRITICAL_POST_MERGE_SMOKE, got FAILz4%(py2)s
{%(py2)s = %(py0)s.smoke_status
} == %(py5)sr   z expected smoke_status=FAIL, got r|   r}   r   )rS   r7   r"   tupler   r   r   r   rn   r   r   r   r   r   r   r   r   r   r   r   r   smoke_statusrU   	smoke_cmdr5   rj   allowed_decisionr   r   r   r   r   r   r   r   r   s                 r!   "test_tc10_post_merge_smoke_failurer     s3    ;I%."/q1Ui"2PQ	 F /C %#,	" )!(F ?? ?11   ?1                    2    2    :&//9JK      #<<   #<                     $=    $=    AAUAU@VW      & &(  &                  #)    +6+>+>*?@     r#   c           	        g d}|| _         t        dt        dd      t        |      t        dd      i      }t	        |d|dd	
      }t        t        ddd      }d!d}t        |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}}|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z   d |iz  }t%        t        j&                  |            dx}x}}y)"u^   squash merge + smoke 모두 PASS → verify_head_lock_then_merge가 AUTO_MERGE_SUCCESS 반환.r   r   r   r   r   z5 passed, 0 failedNTr   r   r   rH   sha-pr-pass-001r   c                     y)Nr   r)   r   s    r!   r   z2test_tc11_post_merge_smoke_pass.<locals>._same_sha8  r   r#   Fr   rk   rm   r   r   ro   z!expected AUTO_MERGE_SUCCESS, got r   rs   rt   PASSr   r   z expected smoke_status=PASS, got r|   r}   r   )rS   r7   r"   r   r   r   r   r   rn   r   r   r   r   r   r   r   r   r   r   r   r   r   s                 r!   test_tc11_post_merge_smoke_passr     sO   :I%."/q1Ui"2FG	 F /C %#+	! )!(F ?? ?00   ?0                    1    1    ,FOO+<Jv}}oV      & &(  &                  #)    +6+>+>*?@     r#   c                    ddl d%fd} t         | d       | d       | d       | d      d      }d	d
id	did	did	dig}t        ||      }|D ci c]  }|d	   |
 }}|d
   }|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 }	|	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   }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   }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   }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 }	|	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c c}w )&uy  recheck_following_prs 호출 시 mergeStateStatus에 따라
    needs_recheck/behind/conflict/blocked 필드가 올바르게 설정돼야 한다.

    케이스:
      pr_number=55 : BEHIND  → needs_recheck=True, behind=True
      pr_number=56 : DIRTY   → conflict=True
      pr_number=77 : BLOCKED → blocked=True
      pr_number=88 : CLEAN   → needs_recheck=False
    r   Nc                H    | ddd}t        dj                  |            S )Nzsha-stubr:   r;   r   r   )r"   dumps)rA   payloadjsons     r!   _make_gh_responsezHtest_tc12_recheck_following_prs_state_machine.<locals>._make_gh_responseZ  s&    '-ZX^_Qtzz'':;;r#   r   r   r   r9   ))55)56)77)88rc   7   8   M   X   )queuer5   needs_recheckT)is)z%(py1)s is %(py4)s)ry   rr   z*PR#55 BEHIND: needs_recheck must be True: rs   rt   behindz#PR#55 BEHIND: behind must be True: conflictFz&PR#55 BEHIND: conflict must be False: z$PR#56 DIRTY: conflict must be True: z)PR#56 DIRTY: needs_recheck must be True: blockedz%PR#77 BLOCKED: blocked must be True: z+PR#77 BLOCKED: needs_recheck must be True: z*PR#88 CLEAN: needs_recheck must be False: z#PR#88 CLEAN: behind must be False: z%PR#88 CLEAN: conflict must be False: z$PR#88 CLEAN: blocked must be False: )rA   r~   r   subprocess.CompletedProcess)	r  r7   r   r   r   r   r   r   r   )r  r5   r  statessby_prs55r   r   r   r   r   s56s77s88r  s                  @r!   -test_tc12_recheck_following_prs_state_machiner  N  s    < "8,"7+"9-"7+	 F 
b	b	b	b	E #v>F(./1Q{^Q/E/ )C[4[4'[[[4[[[[[[4[[[+UVYUZ)[[[[[[[[x=MDM=D MMM=DMMM=MMMDMMM$Gu"MMMMMMMMz?SeS?e#SSS?eSSS?SSSeSSS'McU%SSSSSSSS )Cz?PdP?d"PPP?dPPP?PPPdPPP&J3%$PPPPPPPPZ4Z4'ZZZ4ZZZZZZ4ZZZ+TUXTY)ZZZZZZZZ )Cy>PTP>T!PPP>TPPP>PPPTPPP%J3%#PPPPPPPP\4\4'\\\4\\\\\\4\\\+VWZV[)\\\\\\\\ )C\5\5(\\\5\\\\\\5\\\,VWZV[*\\\\\\\\x=NEN=E!NNN=ENNN=NNNENNN%H#NNNNNNNNz?ReR?e#RRR?eRRR?RRReRRR'LSE%RRRRRRRRy>PUP>U"PPP>UPPP>PPPUPPP&J3%$PPPPPPPP/ 0s   [?c                 ~    t        j                  t        d      5  t        g d       ddd       y# 1 sw Y   yxY w)uN   assert_no_forbidden_git_flags(['gh','pr','merge','--admin']) → RuntimeError.FORBIDDEN_GIT_FLAGSmatch)r   r   r   z--adminNr   raisesRuntimeErrorr   r)   r#   r!   test_bonus1_admin_flag_raisesr#    s3    	|+@	A H%&FGH H H   3<c                 ~    t        j                  t        d      5  t        g d       ddd       y# 1 sw Y   yxY w)uH   assert_no_forbidden_git_flags(['git','rebase','main']) → RuntimeError.REBASE_FORBIDDENr  )r   r   r:   Nr   r)   r#   r!   test_bonus2_rebase_raisesr'    s3    	|+=	> A%&?@A A Ar$  )r    r(  )r   r   r   r~   r   r~   r   r  )r4   dict)r   r   )NNNr   N)rW   z
str | NonerY   r~   r   r   )rU   r   r   None)r   r*  )E__doc__
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   r   syspathlibr   r   __file__resolveparent	WORKSPACEr~   r   removeinsertutils.merge_queue_executorr   r   r   r   r	   r
   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r"   r7   rT   r   r   r   fixturerU   r[   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r#  r'  r)   r#   r!   <module>r;     s  0 #     
   N""$++2299	y>SXXHHOOC	N# 3y> "     6e
. $8  !  )R@2t<	   !2 	 ._@.%SV_F:@46H4@1n+b7QzHAr#   