
    rjK                       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mZ  ee      j!                         j"                  d   Z ee      e
j(                  vr"e
j(                  j+                  d ee             ddlmZmZmZmZmZmZ ddlmZmZm Z m!Z!m"Z" dd	l#m$Z$ d
Z%dZ&ddddZ'ddd dZ(d!dZ)d"dZ*d"dZ+d"dZ,d"dZ-d"dZ.d"dZ/d"dZ0d"dZ1d"dZ2 G d d      Z3d"dZ4y)#u  tests/regression/test_reconcile_evidence_contract_2624.py — task-2625 회귀 테스트.

회장 §7 verbatim 10건 + 보너스 1건.

- §7-1.  post-merge-smoke PASS → _gather가 "PASS" 반환
- §7-2.  FAIL → "FAIL" 반환
- §7-3.  TIMEOUT → "TIMEOUT" 반환 + PASS 아님 + allows_finalize=False
- §7-4.  BLOCKED → "BLOCKED" 반환 + PASS 아님 + allows_finalize=False
- §7-5.  SKIPPED → determine_state FINALIZED 반환 안 함  (PASS 대조군은 FINALIZED)
- §7-6.  역할 분리: qc-result만 있으면 _gather→None; 두 path가 다름
- §7-7.  backward compat: post_merge_smoke_status=None → 기존 FINALIZED 흐름 불변
- §7-8.  substring false-positive 방지: smoke_status=FAIL, stderr에 "PASS" 포함 → "FAIL"
- §7-9.  missing evidence → None + allows_finalize(None)=True
- §7-10. malformed evidence (깨진 JSON / 잘못된 schema_version / 잘못된 status enum) → 모두 None
- bonus. writer schema 12키 + redaction round-trip
    )annotationsN)Path)Any   ) POST_MERGE_SMOKE_EVIDENCE_SCHEMAPostMergeSmokeRunSmokeStatusbuild_post_merge_smoke_evidencepost_merge_smoke_evidence_pathwrite_post_merge_smoke_evidence)LifecycleEvidenceLifecycleState_gather_post_merge_smoke_statusdetermine_state post_merge_smoke_allows_finalize)SmokeResultztask-2625-test(deadbeefdeadbeefdeadbeefdeadbeefdeadbeef stderr_tailc               ,   t        d| t        j                  k(  | t        j                  k(  rdndd|| t        j                  k(  rdn| j                        }t	        t
        t        | |dg d| t        j                  t        j                  fv dd	d	

      S )u9   PostMergeSmokeRun fixture — 실제 smoke 실행 없음.zpytest tests/ -qr      z1 passedN)commandpassed	exit_codestdout_tailr   failure_reason{   pytestztests/z-qF)
merge_committask_idstatussmoke_resultduration_mssmoke_commandallow_continuation
escalationstaledry_run)r   r	   PASSvaluer   _MERGE_COMMIT_TASK_IDSKIPPED)r#   r   srs      M/home/jay/workspace/tests/regression/test_reconcile_evidence_contract_2624.py_make_smoke_runr2   =   s    	"+***!1!11!q%)9)99tv||
B "0"{'7'79L9L&MM     F
bad_schemac                   | dz  dz  }|j                  dd       |t         dz  }|rdnt        }||t        t        dg dd	d
d
dddd}|j	                  t        j                  |      d       |S )uM   post-merge-smoke.json 을 tmp_path 기반으로 직접 작성 (subprocess 0).memoryeventsTparentsexist_ok.post-merge-smoke.jsonzwrong_schema.v12026-05-21T00:00:00+00:00r   r   r   NFschema_versionsmoke_statusr!   r"   tsr&   r%   r   r   r   r)   r'   utf-8encoding)mkdirr.   r   r-   
write_textjsondumps)tmp_path
status_strr5   ev_dirpathschemapayloads          r1   _write_evidencerO   U   s     8+F
LLL-xj 677D",2RF "%)3"G 	OODJJw''O:Kr3   c                     t        di dt        dddddt        ddd	d
dddddddddddddddddddddddddddd}|j                  |        t	        di |S ) uT   FINALIZED 가능한 베이스 evidence 생성 — post_merge_smoke_status 미설정.r"   	pr_numberc   pr_stateMERGEDr!   merged_into_mainT	ci_statusSUCCESSr@   r+   timer_status	completedtimer_end_timez2026-05-21T00:00:00Zhas_donehas_done_ackedhas_merge_donehas_qc_resulthas_followupFhas_escalate_markerescalate_marker_age_minutesNtelegram_reply_truncatedbot_session_statusokworktree_existsbranch_pushed_to_remote )dictr.   r-   updater   )	overridesbases     r1   _make_finalized_base_evidencerl   m   s       #	
    ! .      "  %)!" "'#$  %& '( !&)D, 	KK	$t$$r3   c                   t        | d       t        t        |       }d}||k(  }|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}}y	)
uK   §7-1: post-merge-smoke PASS → _gather_post_merge_smoke_status == 'PASS'.r+   ==z%(py0)s == %(py3)sresultpy0py3zExpected 'PASS', got 
>assert %(py5)spy5NrO   r   r.   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgAssertionError_format_explanationrI   rq   @py_assert2@py_assert1@py_format4@py_format6s         r1   test_gather_pass_statusr          Hf%,Xx@F?6V???6V??????6???6???V???4VJ???????r3   c                   t        | d       t        t        |       }d}||k(  }|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}}y	)
uK   §7-2: post-merge-smoke FAIL → _gather_post_merge_smoke_status == 'FAIL'.FAILrn   rp   rq   rr   zExpected 'FAIL', got ru   rv   Nrw   r   s         r1   test_gather_fail_statusr      r   r3   c                j   t        | d       t        t        |       }d}||k(  }|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}}d
}||k7  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}t        d      }t        |      }d}||u }|st        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  }	t        j                  d      dz   d|	iz  }
t        t        j                  |
            d	x}x}}y	)uH   §7-3: TIMEOUT → 'TIMEOUT' 반환, PASS 아님, allows_finalize=False.TIMEOUTrn   rp   rq   rr   zExpected 'TIMEOUT', got ru   rv   Nr+   !=z%(py0)s != %(py3)sassert %(py5)spost_merge_smoke_statusFisz0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} is %(py6)sr   evidencers   py1rt   py6zTIMEOUT should block finalize
>assert %(py8)spy8rO   r   r.   rx   ry   rz   r{   r|   r}   r~   r   r   rl   r   rI   rq   r   r   r   r   r   @py_assert5@py_assert4@py_format7@py_format9s              r1   .test_gather_timeout_status_and_blocks_finalizer         Hi(,Xx@FE6YEEE6YEEEEEE6EEE6EEEYEEE":6* EEEEEEE6V6V66V,YOH+H5 ( (5> ( (''(5 ( (!'( ('' , ( ('i , ( (!'( ('' -5 ( ('i -5 ( ('i 6 ( ('i :? ( ('''( ( (''( ( (r3   c                j   t        | d       t        t        |       }d}||k(  }|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}}d
}||k7  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}t        d      }t        |      }d}||u }|st        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  }	t        j                  d      dz   d|	iz  }
t        t        j                  |
            d	x}x}}y	)uH   §7-4: BLOCKED → 'BLOCKED' 반환, PASS 아님, allows_finalize=False.BLOCKEDrn   rp   rq   rr   zExpected 'BLOCKED', got ru   rv   Nr+   r   r   r   r   Fr   r   r   r   r   zBLOCKED should block finalizer   r   r   r   s              r1   .test_gather_blocked_status_and_blocks_finalizer      r   r3   c                   t        d      }t        |      \  }}t        j                  }||k(  }|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                  t              rt	        j                  t              ndt	        j                  |      dz  }t	        j                  d|       d	z   d
|iz  }t        t	        j                  |            dx}}t        d      }t        |      \  }	}t        j                  }|	|k7  }|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                  t              rt	        j                  t              ndt	        j                  |      dz  }t	        j                  d|	       d	z   d
|iz  }t        t	        j                  |            dx}}y)u@   §7-5: SKIPPED → FINALIZED 안 됨. PASS → FINALIZED 확인.r+   r   rn   z1%(py0)s == %(py4)s
{%(py4)s = %(py2)s.FINALIZED
}
state_passr   rs   py2py4z!PASS should yield FINALIZED, got 
>assert %(py6)sr   Nr/   r   )z1%(py0)s != %(py4)s
{%(py4)s = %(py2)s.FINALIZED
}state_skippedz&SKIPPED must NOT yield FINALIZED, got )rl   r   r   	FINALIZEDrx   ry   rz   r{   r|   r}   r~   r   r   )
rI   ev_passr   _@py_assert3r   @py_format5r   
ev_skippedr   s
             r1   (test_skipped_blocks_finalize_pass_allowsr      s    ,FKG#G,MJ'11 9:11 9'8'89:1 9 928&9 9 8 8  9 9/8y  9 928&9 9 8 8 ( 9 9/8y ( 9 9/8y 2 9 9'8'8
+J<89 9 9%8%89 9 /yQJ&z2M1*44 A=44 A/@/@A=4 A A:@&A A(@(@  A A7@y  A A:@&A A(@(@ + A A7@y + A A7@y 5 A A/@/@
0@A A A-@-@A Ar3   c                   | dz  dz  }|j                  dd       |t         dz  }|j                  dd       t        t        |       }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        t        |       }||k7  }|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z   d|	iz  }
t        t	        j                  |
            d	}|j                  }|j                  }d} ||      }|sddt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      t	        j                  |      t	        j                  |      dz  }t        t	        j                  |            d	x}x}x}}|j                  }|j                  }d} ||      }|sddt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      t	        j                  |      t	        j                  |      dz  }t        t	        j                  |            d	x}x}x}}y	)uz   §7-6: qc-result 파일만 있으면 _gather_post_merge_smoke_status는 None.
    두 파일 경로가 다름 확인.
    r7   r8   Tr9   z
.qc-resultr+   rB   rC   Nr   z%(py0)s is %(py3)srq   rr   u=   qc-result만 존재할 때 _gather는 None이어야 함, got ru   rv   workspace_rootr   )z%(py0)s != %(py2)s
smoke_pathqc_pathrs   r   uB   post-merge-smoke.json과 qc-result는 서로 다른 path여야 함
>assert %(py4)sr   r<   zdassert %(py8)s
{%(py8)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.name
}.endswith
}(%(py6)s)
})rs   r   r   r   r   )rE   r.   rF   r   rx   ry   rz   r{   r|   r}   r~   r   r   r   nameendswith)rI   rK   r   rq   r   r   r   r   r   @py_format3r   r   r   @py_assert7r   s                  r1   'test_role_separation_qc_result_not_readr      s   
  8+F
LLL-(:..Gv0 -Xx@F S6T> SARARS6T S SLRFS S:R:R  S SIR  S SIR  S SARAR
GzRS S S?R?RS S 0RJ fff:ffffff:fff:fffffffffffff"fffffff??=?##=$<=#$<========:===:===?===#===$<==========<<.<  .. ........7...7...<... .............r3   c                l   t        d      }t        |      }d}||u }|st        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  }t        j                  d	      d
z   d|iz  }t        t        j                  |            dx}x}}t        |      \  }}t        j                  }	||	k(  }
|
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                  t              rt        j                  t              ndt        j                  |	      dz  }t        j                  d|       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      dz   d|iz  }t        t        j                  |            dx}
x}	}y)uI   §7-7: post_merge_smoke_status=None이면 기존 FINALIZED 흐름 불변.Nr   Tr   r   r   evr   uC   post_merge_smoke_status=None이면 finalize 허용(backward compat)r   r   rn   r   stater   r   u<   post_merge_smoke_status=None이면 FINALIZED여야 함, got r   r   r+   )z4%(py2)s
{%(py2)s = %(py0)s.smoke_status
} == %(py5)s)rs   r   rv   u   evidence.smoke_status 불변z
>assert %(py7)spy7)rl   r   rx   ry   rz   r{   r|   r}   r~   r   r   r   r   r   r@   )rI   r   r   r   r   r   r   r   r   r   r   r   r   @py_format8s                 r1   *test_backward_compat_none_post_merge_smoker      s    
't	DB ,B/ N4 N/47 N N<M<MN/4 N NGMvN N5M5M , N NDMI , N NGMvN N5M5M -/ N NDMI -/ N NDMI 0 N NDMI 48 N N<M<MMN N N:M:MN N r"HE1",, O5,, O=N=NO5, O OHNO O6N6N  O OENY  O OHNO O6N6N # O OENY # O OENY - O O=N=N
FugNO O O;N;NO O ??DfD?f$DDD?fDDDDDD2DDD2DDD?DDDfDDD&DDDDDDDDr3   c                t   | dz  dz  }|j                  dd       |t         dz  }t        dt        t        ddgd	d
ddddd}|j	                  t        j                  |      d       t        t        |       }d}||k(  }|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}}y)u_   §7-8: smoke_status=FAIL인데 stderr_tail에 'PASS' 단어 포함 → 여전히 'FAIL' 반환.r7   r8   Tr9   r<   r   r=   r    d   r   z4PASS was mentioned in some old test but overall FAILEXIT_1Fr>   rB   rC   rn   rp   rq   rr   uR   smoke_status=FAIL, stderr에 PASS 포함돼도 결과는 'FAIL'이어야 함, got ru   rv   NrE   r.   r   r-   rF   rG   rH   r   rx   ry   rz   r{   r|   r}   r~   r   r   )	rI   rK   rL   rN   rq   r   r   r   r   s	            r1   (test_substring_false_positive_preventionr     sH     8+F
LLL-xj 677D:%)"M"#G 	OODJJw''O:,Xx@F h6V hVgVgh6V h hagagh hOgOg  h h^g^g  h h^g^g  h hVgVg
\]c\fgh h hTgTgh hr3   c                   t        t        |       }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        d	      }t        |      }d
}||u }|st        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  }	t        j                  d      dz   d|	iz  }
t        t        j                  |
            dx}x}}y)uV   §7-9: post-merge-smoke.json 파일 없음 → _gather→None, allows_finalize→True.Nr   r   rq   rr   u)   파일 부재 시 None이어야 함, got ru   rv   r   Tr   r   r   r   u4   evidence None이면 finalize 허용(backward compat)r   r   )r   r.   rx   ry   rz   r{   r|   r}   r~   r   r   rl   r   r   s              r1   6test_missing_evidence_returns_none_and_allows_finalizer   &  s    -Xx@F ?6T> ?->->?6T ? ?8>? ?&>&>  ? ?5>Y  ? ?5>Y  ? ?->->
3F:>? ? ?+>+>? ? -TJH+H5 ? ?5= ? ?->->?5 ? ?8>? ?&>&> , ? ?5>Y , ? ?8>? ?&>&> -5 ? ?5>Y -5 ? ?5>Y 6 ? ?5>Y :> ? ?->->>? ? ?+>+>? ? ?r3   c                  8    e Zd ZdZddZddZddZddZddZy)	TestMalformedEvidenceuP   §7-10: 깨진 JSON / 잘못된 schema_version / 잘못된 status enum → None.c                   |dz  dz  }|j                  dd       |t         dz  }|j                  dd       t        t        |      }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}}y	)u   깨진 JSON → None.r7   r8   Tr9   r<   z{not valid json{{{{rB   rC   Nr   r   rq   rr   u'   깨진 JSON → None이어야 함, got ru   rv   )rE   r.   rF   r   rx   ry   rz   r{   r|   r}   r~   r   r   	selfrI   rK   rL   rq   r   r   r   r   s	            r1   test_broken_json_returns_nonez3TestMalformedEvidence.test_broken_json_returns_none9  s    H$x/TD18*$:;;-@08DSv~SSSvSSSSSSvSSSvSSSSSS!H
SSSSSSSr3   c                   t        |dd       t        t        |      }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}}y)u"   잘못된 schema_version → None.r+   Tr4   Nr   r   rq   rr   u4   schema_version 불일치 → None이어야 함, got ru   rv   rw   )r   rI   rq   r   r   r   r   s          r1   &test_wrong_schema_version_returns_nonez<TestMalformedEvidence.test_wrong_schema_version_returns_noneC  s    &T:08D 	Nv~ 	N<M<M	Nv 	N 	NGMv	N 	N5M5M  	N 	NDMI  	N 	NDMI  	N 	N<M<MB6*M	N 	N 	N:M:M	N 	Nr3   c                p   |dz  dz  }|j                  dd       |t         dz  }t        dt        t        ddd	d
d
dddd}|j	                  t        j                  |      d       t        t        |      }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}}y)u6   알 수 없는 smoke_status 값 → None (5 enum 외).r7   r8   Tr9   r<   UNKNOWN_STATUSr=   Nr   r   Fr>   rB   rC   r   r   rq   rr   u6   알 수 없는 status enum → None이어야 함, got ru   rv   r   
r   rI   rK   rL   rN   rq   r   r   r   r   s
             r1   %test_invalid_status_enum_returns_nonez;TestMalformedEvidence.test_invalid_status_enum_returns_noneJ  s>   H$x/TD18*$:;;>,)-!""'
 	

7+g>08D 	Pv~ 	P>O>O	Pv 	P 	PIO	P 	P7O7O  	P 	PFOi  	P 	PFOi  	P 	P>O>ODVJO	P 	P 	P<O<O	P 	Pr3   c                >   |dz  dz  }|j                  dd       |t         dz  }|j                  t        j                  ddg      d	       t        t        |      }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}}y
)u-   JSON이 dict가 아닌 경우(list) → None.r7   r8   Tr9   r<   r+   r   rB   rC   Nr   r   rq   rr   u/   JSON이 list일 때 → None이어야 함, got ru   rv   )rE   r.   rF   rG   rH   r   rx   ry   rz   r{   r|   r}   r~   r   r   r   s	            r1   test_non_dict_json_returns_nonez5TestMalformedEvidence.test_non_dict_json_returns_nonec  s   H$x/TD18*$:;;

FF#34wG08D 	Iv~ 	I7H7H	Iv 	I 	IBH&	I 	I0H0H  	I 	I?Hy  	I 	I?Hy  	I 	I7H7H=fZH	I 	I 	I5H5H	I 	Ir3   c                ^   |dz  dz  }|j                  dd       |t         dz  }t        t        t        d}|j	                  t        j                  |      d       t        t        |      }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}}y	)u(   smoke_status 키 자체 없음 → None.r7   r8   Tr9   r<   )r?   r!   r"   rB   rC   Nr   r   rq   rr   u7   smoke_status 키 없을 때 → None이어야 함, got ru   rv   r   r   s
             r1   *test_missing_smoke_status_key_returns_nonez@TestMalformedEvidence.test_missing_smoke_status_key_returns_nonen  s#   H$x/TD18*$:;;>)	
 	

7+g>08D 	Qv~ 	Q?P?P	Qv 	Q 	QJP&	Q 	Q8P8P  	Q 	QGPy  	Q 	QGPy  	Q 	Q?P?PEfZP	Q 	Q 	Q=P=P	Q 	Qr3   NrI   r   )	__name__
__module____qualname____doc__r   r   r   r   r   rg   r3   r1   r   r   6  s#    ZTNP2	IQr3   r   c                z   d}t        t        j                  |      }t        |      }h d}t	        |j                               }||k(  }|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	||z
   d
||z
         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}|	|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   }|	|v }
|
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   }|	|v}
|
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}
}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)*            }|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}|	|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   }|	|v }
|
slt        j                  d|
fd|	|f      t        j                  |	      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}	x}
}t+        t,        | $      }||k(  }|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-|       dz   d|iz  }t        t        j                  |            d}y).uF   Bonus: build_post_merge_smoke_evidence 12키 검증 + token redaction.z5Authorization: ghp_ABCDEFGHIJKLMNOPQRSTabcdefghijklmnr   >   rA   r)   r"   r%   r   r   r!   r@   r&   r   r?   r'   rn   )z%(py0)s == %(py2)sactual_keysexpected_keysr   u    12키 불일치. 추가된 키: u   , 누락 키: r   r   Nr?   )z%(py1)s == %(py3)sr   )r   rt   r   rv   r@   r+   )z%(py1)s == %(py4)s)r   r   zassert %(py6)sr   z
[REDACTED]r   )in)z%(py1)s in %(py4)su3   ghp_ 토큰이 [REDACTED]로 redaction되어야 함r   ghp_)not in)z%(py1)s not in %(py4)su7   원본 토큰이 stderr_tail에 남아있으면 안 됨r   u=   write_post_merge_smoke_evidence가 파일을 생성해야 함zC
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}written_pathr   rB   rC   expected_pathu   기록 경로 불일치: z != )r2   r	   r+   r
   setkeysrx   ry   rz   r{   r|   r}   r~   r   r   r   r   existsrG   loads	read_textr   r.   )rI   token_stderrrunevidence_dictr   r   r   r   r   @py_assert0r   r   r   r   r   r   reloadedr   s                     r1   (test_writer_schema_12_keys_and_redactionr     s    KL
+**
EC3C8MM m((*+K-' tbsbst;- t tmsmst t[s[s  t tjsjs  t tmsmst t[s[s ( t tjsjs ( t tbsbs
*;+F*G~VcfqVqUrst t t`s`st t )*N*.NNNNN*.NNNN*NNNNNN.NNNN.NNNNNNNN (2F2(F2222(F222(222F2222222  >=7 ><77 >,=,=><7 > >4=I  > >4=I 8 > >,=,==> > >*=*=> > B}5 B655 B0A0AB65 B B8A	  B B8A	 6 B B0A0AAB B B.A.AB B 33xPLa a aa"aaaaaaa<aaa<aaaaaa aaaaaazz,00'0BCH$%I%)IIIII%)IIII%IIIIII)IIII)IIIIIIIIN#-v-#v----#v---#---v-------28M22<22222<2222<22222222222 38HUM=( F4E4EF<= F F?EvF F-E-E  F F<EI  F F?EvF F-E-E ) F F<EI ) F F4E4E
#L>m_EF F F2E2EF Fr3   )r#   r	   r   strreturnr   )rI   r   rJ   r   r5   boolr   r   )r   r   r   )5r   
__future__r   builtinsrz   _pytest.assertion.rewrite	assertionrewriterx   rG   syspathlibr   typingr   __file__resolver:   _WORKTREE_ROOTr   rL   insertutils.post_merge_smoke_runnerr   r   r	   r
   r   r   &utils.lifecycle_reconciliation_managerr   r   r   r   r   utils.automation_contractsr   r.   r-   r2   rO   rl   r   r   r   r   r   r   r   r   r   r   r   rg   r3   r1   <module>r
     s   " #    
   h'')11!4~chh&HHOOAs>*+   3  @B 0 LQ 0%@@@	( 	( A&/4E,h@	? GQ GQ\3Fr3   