
     j]                    :   U d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	m
Z
m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# ddl$m%Z%m&Z& ddl'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z- dAdBd	Z.dCd
Z/dDdZ0ddddddddZ1de2d<   ddddddddZ3de2d<   ddddddddZ4de2d<   dddddd ddZ5de2d!<   d"dddd#d$ddZ6de2d%<   d&Z7d'Z8d(Z9d) Z:d* Z;d+ Z<d, Z=dEd-Z>d. Z?d/ Z@d0 ZAd1 ZBd2 ZCd3 ZDd4 ZEd5 ZFd6 ZGd7 ZHd8 ZId9 ZJd: ZKd; ZLd< ZMd= ZNd> ZOd? ZPd@ ZQy)Fu  tests/regression/test_bot_merge_identity_2522.py

회귀 테스트 — task-2522 GitHub App bot merge identity hardening.

회장 §본질:
  task-2522의 목표는 "머지가 되게 하는 것"이 **아니다.** 이미 머지는 된다.
  목표는 **"누가 머지했는가"**를 자동화 기준에 맞게 고치는 것.
  owner_pat로 머지되면 functional success여도 autonomy success로 인정 X.

5 연속 mergedBy=JonghyukJeon (PR #68/#69/#70/#71/#72) 종결 박제 + token source 4 enum 분류.

영역 A — Token source 분류 (4건)
영역 B — owner_pat fallback fixture replay (PR #68~#72, 5건)
영역 C — capability probe + autonomy_score 변화 (5건)

총 14건. 회장 §6 단일 파일.

회장 §보안: raw token value를 어떤 형태로도 출력/기록 X. fixture token도 prefix만 사용.
gh api 실호출 금지 — 모두 runner mock / dataclass 직접 주입.
    )annotationsN)Path)AnyDictList)DEFAULT_AUDIT_JSONL_PATHMergeIdentityAuditRecordTokenSourceTOKEN_SOURCE_GITHUB_APPTOKEN_SOURCE_GITHUB_ACTIONSTOKEN_SOURCE_OWNER_PATTOKEN_SOURCE_UNKNOWNappend_audit_jsonlbuild_audit_recordclassify_token_sourcecompute_autonomy_score_deltadecide_autonomy_capability_gapexpected_bot_identity_for_actorfingerprint_token_for_auditprobe_token_source_from_env)OWNER_PAT_FALLBACK_BLOCKEDselect_merge_token_decision)BlockedReasonRepositoryCapabilityclassify_capability_gapprobe_bot_merge_identity*reevaluate_bot_can_merge_with_token_sourceselect_merge_pathc                4    t        j                  g | ||      S )N)args
returncodestdoutstderr)
subprocessCompletedProcess)r!   r"   r#   s      D/home/jay/workspace/tests/regression/test_bot_merge_identity_2522.pycpr'   H   s    &&B:f]cdd    c                     dd fd}|S )uL   gh pr view <N> --json number,mergedBy 호출에 응답하는 runner factory.c                &   ~t        |       dk  s
| dd g dk7  rt        ddd      S 	 t        | d         }j	                  |      }|t        ddd	      S t        dt        j                  |      d      S # t        $ r t        ddd      cY S w xY w)
N   r      )ghprview    zunexpected callzbad pr numberz	Not Found)lenr'   int
ValueErrorgetjsondumps)r    cwdnpayloadpr_payloadss       r&   runnerz#make_pr_view_runner.<locals>.runnerO   s    t9q=D1I)==a.//	.DGA //!$?a[))!TZZ("--  	.a_--	.s   A7 7BBN)r    z	List[str]r8   r   returnsubprocess.CompletedProcess )r;   r<   s   ` r&   make_pr_view_runnerrA   L   s    . Mr(   c                 \    t        dddddd      }|j                  |        t        di |S )NTF)can_squash_mergerequires_approvalrequires_thread_resolutionauto_merge_enabledbot_can_mergeadmin_override_requiredr@   )dictupdater   )	overridesbases     r&   make_capabilityrM   _   s:    #( %D 	KK	'$''r(   D   JonghyukJeonUser)logintypez	task-2517z2026-05-09T00:53:24Z)task_idmergedAt)numbermergedBy_metaDict[str, Any]PR_68_FIXTUREE   z	task-2519z2026-05-09T02:04:50ZPR_69_FIXTUREF   z	task-2518z2026-05-09T04:50:46ZPR_70_FIXTUREG   z	task-2520z2026-05-09T06:31:34ZPR_71_FIXTUREH   	task-2521z2026-05-09T13:30:00ZPR_72_FIXTUREghs_aaaaghp_zzzzgithub_pat_yyyc                    t        t        dt        i      } | j                  t        k(  sJ | j                  du sJ | j
                  du sJ | j                  t        dd k(  sJ | j                  J t        | j                        dk(  sJ t        j                  | j                               }t        dd |vsJ d       y)	u[   (1) ``ghs_`` prefix token + actions runner 신호 없음 → GITHUB_APP_INSTALLATION_TOKEN.GITHUB_APP_INSTALLATION_TOKENtoken_valueenvTFN      z-raw token suffix leaked into serialised probe)r   "_FIXTURE_INSTALLATION_TOKEN_PREFIXtoken_sourcer   installation_signalactions_runner_signaltoken_prefix_observedtoken_fingerprint_sha256_8r2   r6   r7   to_dictprobe
serialiseds     r&   >test_a1_ghs_prefix_classified_as_github_app_installation_tokenrw      s    !6,.PQE !8888$$,,,&&%///&&*LRa*PPPP++777u//0A555EMMO,J-ab1C 7Cr(   c                     t        t        dt        i      } | j                  t        k(  sJ | j                  du sJ t        t
        dt
        i      }|j                  t        k(  sJ |j                  du sJ y)u9   (2) ``ghp_`` 또는 ``github_pat_`` prefix → OWNER_PAT.
GITHUB_PATrh   TGH_PATN)r   !_FIXTURE_OWNER_PAT_CLASSIC_PREFIXrn   r   owner_pat_signal%_FIXTURE_OWNER_PAT_FINEGRAINED_PREFIXprobe_aprobe_bs     r&   9test_a2_ghp_and_github_pat_prefix_classified_as_owner_patr      s     $5<=G #9999##t+++ $9<=G #9999##t+++r(   c                     t        dddd      } | j                  t        k(  sJ | j                  du sJ t        t        dt        d      }|j                  t        k(  sJ |j                  du sJ y)u   (3) GITHUB_ACTIONS=true env (token 값 없음) → GITHUB_ACTIONS_TOKEN.

    runner GITHUB_TOKEN(ghs_)가 있어도 actions runner 환경에서는
    ACTIONS_TOKEN으로 분류된다 (출처가 명확).
    Ntruezubuntu-runner-1)GITHUB_ACTIONSRUNNER_NAMErh   T)r   GITHUB_TOKEN)r   rn   r   rp   rm   r~   s     r&   6test_a3_github_actions_env_classified_as_actions_tokenr      s     $%6GHG #>>>>((D000 $6%7YZG #>>>>((D000r(   c                 .   t        di       } | j                  t        k(  sJ | j                  du sJ | j                  du sJ | j
                  du sJ | j                  J | j                  J t        | j                        t        j                  u sJ y)u0   (4) env 미설정, token 값 없음 → UNKNOWN.Nrh   F)
r   rn   r   rp   ro   r|   rq   rr   r
   UNKNOWN)ru   s    r&   (test_a4_no_signals_classified_as_unknownr      s    !d;E!5555&&%///$$---!!U***&&...++333u))*k.A.AAAAr(   c                   t        | |i      }t        dd| g|      }|j                  du sJ |j                  dk(  sJ |j                  du sJ |j
                  dk(  sJ |j                  du sJ t        |      }|t        j                  k(  sJ t        | t        t        dt        i	      |d
   d   |d
   d   |j                  di       j                  d      |j                  di       j                  d            }|j                  t        k(  sJ |j                   dk(  sJ |j"                  du sJ |j$                  du sJ |j&                  du sJ y)uS   공통 검증: PR fixture replay → owner_pat fallback 박제 + autonomy gap True.Jeon-Jonghyukdev_workspacer<   T	owner_patFrO   ry   rh   rV   rQ   rR   rW   mergeCommitrS   )	pr_numbertoken_probemergedBy_loginmergedBy_typemerge_commit_sharS   N)rA   r    fallback_to_owner_token_detectedrn   bot_can_merge_as_appmerge_actor_loginmerge_actor_is_botr   r   AUTOMATION_CAPABILITY_GAPr   r   r{   r5   token_source_usedr   r   mergedBy_is_botexpected_bot_identityautonomy_capability_gap)r   fixturer<   identityblockedrecords         r&   _replay_pr_owner_pat_fallbackr      s    )W!56F'9+fH 44<<<  K///((E111%%777&&%///%h/Gm=====  )9@A
 z*73j)&1 Wb155mDGR(,,Y7
F ##'====  N222!!U***''5000))T111r(   c                 $    t        dt               y)u>   (5) PR #68 mergedBy=JonghyukJeon — task-2517 fixture replay.rN   N)r   rY   r@   r(   r&   'test_b1_pr_68_owner_pat_fallback_replayr         !"m4r(   c                 $    t        dt               y)u>   (6) PR #69 mergedBy=JonghyukJeon — task-2519 fixture replay.rZ   N)r   r[   r@   r(   r&   'test_b2_pr_69_owner_pat_fallback_replayr     r   r(   c                 $    t        dt               y)u>   (7) PR #70 mergedBy=JonghyukJeon — task-2518 fixture replay.r\   N)r   r]   r@   r(   r&   'test_b3_pr_70_owner_pat_fallback_replayr     r   r(   c                 $    t        dt               y)u>   (8) PR #71 mergedBy=JonghyukJeon — task-2520 fixture replay.r^   N)r   r_   r@   r(   r&   'test_b4_pr_71_owner_pat_fallback_replayr     r   r(   c                    t        dt               t        t        t        t
        t        t        d      } t        ddg d|       }t        |j                        dk(  sJ t        d |j                  D              sJ |j                  du sJ |j                  d	u sJ t        d
dit               t        j                         }|j"                  dk(  sJ |j$                  d	u sJ |j&                  du sJ y)u  (9) PR #72 mergedBy=JonghyukJeon — task-2521 fixture replay (본 task의 직전 PR).

    회장 §본질: task-2521 자체가 owner_pat fallback으로 머지되어 5 연속 패턴 종결.
    select_merge_path → escalate_capability_gap. 회장 직접 머지 X (ops 채널만).
    r`   )rN   rZ   r\   r^   r`   r   r   r   rk   c              3  4   K   | ]  }|j                     y wr=   )fallback_to_owner_token).0rs     r&   	<genexpr>z:test_b5_pr_72_owner_pat_fallback_replay.<locals>.<genexpr>0  s     PQq((Ps   TFrU   escalate_capability_gapN)r   rb   rA   rY   r[   r]   r_   r   r2   merge_identity_auditallr   r   r   rM   r   r   actionrequires_chaircapability_gap)r<   r   plans      r&   'test_b5_pr_72_owner_pat_fallback_replayr     s     ""m4 !}-}" F (*>vH x,,-222P(2O2OPPPP44<<<((E111	2//D
 ;;3333%'''$&&&r(   c                    t        di       } | j                  t        k(  sJ t        d      }t	        || j                        }|j
                  du sJ t        | j                        }|d   du sJ |d   d	k(  sJ |d
   du sJ y)u   (10) bot/app token 없음 (UNKNOWN) → reevaluate가 bot_can_merge=False 강제.

    select_merge_token_decision → AUTOMATION_CAPABILITY_GAP (allow_merge=False).
    Nrh   TrG   rn   Fallow_mergedecisionr   r   )r   rn   r   rM   r   rG   r   ru   capreevalr   s       r&   @test_c1_no_bot_app_token_classified_as_automation_capability_gapr   C  s    
 "d;E!5555

-C7%J\J\]F5(((*5+=+=>HM"e+++J#>>>>$%---r(   c                 2   t        t        dt        i      } | j                  t        k(  sJ t	        d      }t        || j                        }|j                  du sJ ||k(  sJ t        | j                        }|d   du sJ |d   dk(  sJ |d	   d
u sJ y)uV   (11) bot/app token 있음 + ruleset 허용 → bot_can_merge 유지, allow_merge=True.rg   rh   Tr   r   r   r   APP_TOKEN_OKr   FN)r   rm   rn   r   rM   r   rG   r   r   s       r&   /test_c2_app_token_with_ruleset_allows_bot_merger   U  s    !6,.PQE !8888

-C7%J\J\]F4'''S==*5+=+=>HM"d***J>111$%...r(   c                 h   t        t        dt        i      } t        d| ddd      }|j                  du sJ t	        d	|
      }|j
                  d	k(  sJ |j                  dk(  sJ |j                  dk(  sJ d|j                  j                         v sd|j                  j                         v sJ yy)u   (12) mergedBy = owner (사람) → autonomy_score 유지 또는 -1.

    회장 §본질: owner 직접 머지는 5 연속 패턴 → score 하락 신호.
    ry   rh   r`   rO   rP   ra   r   r   r   r   rS   T   previous_scorer      r   zowner directN)
r   r{   r   r   r   r   	new_scoredeltareasonlowerru   r   r   s      r&   ,test_c3_owner_merged_score_held_or_decreasedr   i  s    
 "5<=E  %F ))T111(&IE1$$$??a;;"%,,,,...ELLDVDVDX2XXX2X.r(   c                    t        t        dt        i      } t        d| ddd      }|j                  du sJ |j                  d	u sJ |j
                  d	u sJ t        d
|      }|j                  dk(  sJ |j                  dk(  sJ y)u   (13) mergedBy = bot/app + token_source = GITHUB_APP_* → autonomy_score +1.

    회장 §본질: 본 task의 종결 목표 — 다음 PR이 bot으로 머지되면 7→8.
    rg   rh   i  github-actions[bot]Bot	task-2522r   FTr   r   rl   r0   N)	r   rm   r   r   r   r   r   r   r   r   s      r&   1test_c4_bot_merged_with_app_token_score_increasedr     s    
 "6,.PQE  ,F ))U222''4///!!T)))(&IE??a;;!r(   c                 
   t        t        dt        i      } t        | j                        }|d   du sJ |d   t        k(  sJ |d   du sJ t        d      }t        || j                  	      }|j                  du sJ t        d
| ddd      }t        dz  dz  dz  }|j                         r|j                          t        ||      }||k(  sJ |j                         sJ |j                  d      j                         j                         d   }t!        j"                  |      }t        dd |vsJ d       |d   du sJ |d   t$        k(  sJ |j                  d       t'        d|      }	|	j(                  dk(  sJ y)u`  (14) owner_pat fallback → 자동 머지 거부, AUTOMATION_CAPABILITY_GAP marker로 박제.

    회장 §5 명시:
      - owner_pat fallback 발생 → Critical 7종 아님, AUTOMATION_CAPABILITY_GAP marker
      - select_merge_token_decision은 OWNER_PAT_FALLBACK_BLOCKED + capability_gap=True
      - audit JSONL append + 다음 score 산출
    ry   rh   r   Fr   r   Tr   r   r`   rO   rP   r   r   memoryeventsz&test_bot_merge_identity_2522_tmp.jsonl)
audit_pathzutf-8)encodingr   rk   Nz(raw token suffix leaked into JSONL auditr   r   )
missing_okr   r   r   )r   r{   r   rn   r   rM   r   rG   r   _WORKTREE_ROOTexistsunlinkr   	read_textstrip
splitlinesr6   loadsr   r   r   )
ru   r   r   r   r   tmpwritten_pathliner:   r   s
             r&   8test_c5_owner_pat_fallback_blocks_auto_merge_with_markerr     s    "5<=E +5+=+=>HM"e+++J#====$%---

-C7%J\J\]F5(((  %F 8
#h
.1Y
YC
zz|

%f=L3::<<=='=*002==?CDjjG,QR0< 2< ,-555&'+AAAAJJ$J )&IE??ar(   c                 ~    t         D  ch c]  } | j                   }} |t        t        t        t
        hk(  sJ yc c} w )uE   sanity: TokenSource enum 정확히 4종 — 회장 §1 추가 금지.N)r
   valuer   r   r   r   )membervaluess     r&   ,test_sanity_token_source_enum_exact_4_valuesr     sA    )45vfll5F5#	    6s   :c                 R    t        t              } | j                  d      sJ |        y)uR   sanity: default audit path가 memory/orchestration-audit/bot-merge-identity.jsonl.zbot-merge-identity.jsonlN)strr   endswith)ps    r&   5test_sanity_default_audit_jsonl_path_inside_workspacer     s%    $%A::014141r(   c                     d} t        | d| i      }t        d|dd      }t        j                  |j	                         d	      }d
|vsJ d|v sJ y)uV   sanity: build_audit_record 결과 직렬화 시 raw token 5자 이후 부분 노출 X.ghs_aaaaSECRETSUFFIX1234567890rg   rh   r0   r   r   )r   r   r   r   F)ensure_asciiSECRETSUFFIXghs_aN)r   r   r6   r7   rs   )
fake_tokenru   r   rv   s       r&   0test_sanity_no_raw_token_in_record_serialisationr     si    1J!,j9E  ,	F FNN,5AJ+++j   r(   c                     t        dd      du sJ t        dd      du sJ t        dd      du sJ t        dd      du sJ t        d	d      du sJ y
)uM   sanity: expected_bot_identity_for_actor — bot suffix or type==Bot → True.r   r   Tzdependabot[bot]rP   renovaterO   Fr1   N)r   r@   r(   r&   (test_sanity_expected_bot_identity_helperr     sm    *+@%HDPPP*+<fEMMM*:u=EEE*>6BeKKK*2u5>>>r(   c                 0    t        t        dd      du sJ y)uB   sanity: token_source=UNKNOWN → 무조건 GAP=True (fail-closed).r   T)rn   r   r   N)r   r   r@   r(   r&   @test_sanity_decide_autonomy_capability_gap_unknown_is_failclosedr     s'    )), 
	  r(   c                     t        d      } | J t        |       dk(  sJ t        | d       t        d      J t        d      J y)u;   sanity: token fingerprint는 sha256의 첫 8 hex 문자만.ghs_test_value_xyzNrl      r1   )r   r2   r3   )fps    r&   1test_sanity_fingerprint_truncation_to_8_hex_charsr    sR    	$%9	:B>>r7a<<BK&t,444&r*222r(   c                     t        ddi      } | j                  t        k(  sJ t        j                  | j                               }d|vsJ d|vsJ y)uU   sanity: probe_token_source_from_env() 결과는 raw token 값을 보유하지 않음.ry   ghp_abcdefghijk0123456789)rj   abcdefghijk
0123456789N)r   rn   r   r6   r7   rs   rt   s     r&   =test_sanity_probe_token_source_from_env_no_raw_value_returnedr	    sW    '\;V,WXE!7777EMMO,J
***z)))r(   c                     ddl m}   | t              D ch c]  }|j                   }}h d}|j	                  |      sJ d||z
          yc c}w )uB   sanity: MergeIdentityAuditRecord 회장 §3 필수 7 field 존재.r   )fields>   r   	timestampr   r   r   r   r   zrequired fields missing: N)dataclassesr  r	   nameissubset)_fieldsffield_namesexpected_requireds       r&   Ctest_sanity_merge_identity_audit_record_dataclass_7_required_fieldsr    s^    -#*+C#DEa166EKE %%k2 
#$5$C#DE2 Fs   Ac                    dt         fdt        fdt        fdt        fdt        fg} d}| D ]  \  }}t        t        dt        i      }t        |||d	   d
   |d	   d   |j                  di       j                  d            }t        ||      }|j                  dk(  sJ d| d|j                          |j                  } |dk(  sJ y)u   integration: PR #68~#72 5건 모두 owner_pat fallback → score 7→2 (5회 -1).

    회장 §본질 5 연속 패턴 박제. score floor=0 검증.
    rN   rZ   r\   r^   r`   r   ry   rh   rV   rQ   rR   rW   rS   r   r   r   zPR #u   : 기대 -1, 실제    N)rY   r[   r]   r_   rb   r   r{   r   r5   r   r   r   )fixturesscorepr_numfixru   r   r   s          r&   ;test_integration_5pr_consecutive_owner_pat_fallback_patternr  2  s     
]	]	]	]	]H E  %9@A
 $z?73j/&1GGGR(,,Y7
 -E&Q{{b RD0DU[[M"RR    A::r(   )r   r1   r1   )r!   r3   r"   r   r#   r   r>   r?   )r;   zDict[int, Any])r>   r   )r   r3   r   rX   r>   None)R__doc__
__future__r   r6   r$   syspathlibr   typingr   r   r   __file__resolveparentr   r   pathremoveinsertutils.bot_merge_identityr   r	   r
   r   r   r   r   r   r   r   r   r   r   r   r   utils.merge_queue_executorr   r   utils.repository_policy_adapterr   r   r   r   r   r   r'   rA   rM   rY   __annotations__r[   r]   r_   rb   rm   r{   r}   rw   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r	  r  r  r@   r(   r&   <module>r,     s  ( #   
  " "
 h'')0077>>~#(("HHOOC'( 3~& '    " e&
(( (&9$2HI!~  (&9$2HI!~  (&9$2HI!~  (&9$2HI!~  (&9$2HI!~  &0 "$. !(8 %(,&1.
B$ 2F5
5
5
5
'H.$/(Y22/ n5!&?3*.r(   