
    ' j';              	         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mZmZ ddlmZ ddlmZ ddlZ ee      j)                         j*                  j*                  j*                  Z ee      e
j0                  v r!e
j0                  j3                   ee             e
j0                  j5                  d ee             ddlmZmZmZmZm Z  ddl!m"Z"m#Z#m$Z$  ed	d
ddddejJ                        Z&dZ'dddd	 	 	 	 	 	 	 d"dZ(d#dZ) G d d      Z* G d d      Z+ G d d      Z, G d d      Z- G d d      Z. G d d      Z/e0d k(  r% e
jb                   ejd                  ed!g             yy)$u(  tests/regression/test_schedule_id_freshness_2535.py — 회귀 6건.

회장 명시 task-2535 P0 (신호등 sync fix C / 2026-05-10):
  cron schedule_id 신선도 검증 부재 → 오래된 schedule_id 로 잘못된 활성 판정 발생.
  Fix: utils/schedule_id_freshness.py (validator) + lifecycle_reconciliation_manager
  STALE_SCHEDULE_ID stuck case.

회귀 6건 (회장 §명시):
  1. fresh (5분 전 응답)        → classify_freshness = FRESH
  2. stale (90분 전 응답)       → classify_freshness = STALE
  3. missing (history 부재)     → classify_freshness = MISSING
  4. lifecycle stuck STALE_SCHEDULE_ID 분류
  5. chat=6937032012 격리 (다른 chat record 는 freshness 판정에 포함 X)
  6. token raw 0 (validator 출력 / stuck detail 에 ghs_/ghp_/github_pat_ prefix 부재)
    )annotationsN)datetime	timedeltatimezone)Path)Any) SCHEDULE_FRESHNESS_THRESHOLD_MINCHAIRMAN_CHAT_IDclassify_freshnessis_schedule_id_freshschedule_id_age_seconds)LifecycleEvidenceStuckReasondetect_stuck_casesi     
      )tzinfo)ghs_ghp_github_pat_okp)statusresponsepromptc          	         |j                         |||||ddd}| j                  dd      5 }|j                  t        j                  |d      d	z          d d d        y # 1 sw Y   y xY w)
Ni  z/tmp/workspace)tschat_idschedule_idr   r   r   duration_msworkspace_pathautf-8encodingFensure_ascii
)	isoformatopenwritejsondumps)	pathr   r   r    r   r   r   recordfs	            G/home/jay/workspace/tests/regression/test_schedule_id_freshness_2535.py_write_recordr3   5   sn     lln"*	F 
3	) ?Q	

66=>? ? ?s   *A  A)c                     t        di 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 dddd dddd}|j                  |        t        di |S )Ntask_idz	task-9999	pr_numberpr_statemerge_commitmerged_into_mainF	ci_statussmoke_statustimer_statustimer_end_timehas_donehas_done_ackedhas_merge_donehas_qc_resulthas_followuphas_escalate_markerescalate_marker_age_minutestelegram_reply_truncatedbot_session_statusworktree_existsbranch_pushed_to_remote )dictupdater   )	overridesbases     r2   _make_evidencerN   E   s       	
           "  %)!" "'#$  %& '( !&)D, 	KK	$t$$    c                  (    e Zd ZdZddZddZddZy)TestFreshRecordu>   회귀 #1: 마지막 chairman record 가 5분 전이면 FRESH.c                   d}|| dz  }t        |t        t        d      z
  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  }dd|iz  }t        t        j                  |            d x}}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  }dd|iz  }t        t        j                  |            d x}}d}|
|k(  }|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}}y )NABCDEF12.logr   minutesr   r   r   r    r   nowhistory_dirFRESH==z%(py0)s == %(py3)sstatepy0py3assert %(py5)spy5rZ   Tisz%(py0)s is %(py3)sis_freshtokenr3   NOWr   r
   r   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationr   selftmp_pathsidlogr_   @py_assert2@py_assert1@py_format4@py_format6ri   rj   s              r2   test_classify_freshz#TestFreshRecord.test_classify_freshg   sb   C5%ccIa$88BR"%	6 #3CXFuuuu.sCXN%x4x4xx4uuuurO   c                `   d}|| dz  }t        |t        t        d      z
  t        |       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  }dd|iz  }t        t        j                  |            d x}}d}	|	|k  }d}
||
k  }|r|st        j                  d||fd|	||
f      t        j                  |	      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |
      dz  }dd|iz  }t        t        j                  |            d x}	x}x}}
y )N	F12RESH00rT   r   rU   r   r   r    rX   )is not)z%(py0)s is not %(py3)sager`   rc   rd   i"  i6  )<=r   )z%(py1)s <= %(py4)sz%(py4)s <= %(py6)s)py1py4py6assert %(py8)spy8)r3   rl   r   r
   r   rm   rn   ro   rp   rq   rr   rs   rt   )rv   rw   rx   ry   r   rz   r{   r|   r}   @py_assert0@py_assert5@py_assert3@py_format7@py_format9s                 r2   test_age_seconds_for_freshz*TestFreshRecord.test_age_seconds_for_fresht   s   C5%ccIa$88BR"%	' &csIs$s$ss$ sc  S cS     scS   s      c   c   S       rO   c           	        d}|| dz  }t        |t        t        dd      z
  t        |       t	        |t        |      }d}||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  t              rt        j                  t              nd
dt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)u<   Threshold = 60min → 59m59s 전 record 는 여전히 FRESH.BNDRY001rT   ;   )rV   secondsr   rX   r[   r\   zR%(py5)s
{%(py5)s = %(py0)s(%(py1)s, now=%(py2)s, history_dir=%(py3)s)
} == %(py8)sr   rx   rl   rw   ra   r   py2rb   rd   r   assert %(py10)spy10Nr3   rl   r   r
   r   rm   rn   ro   rp   rq   rr   rs   rt   	rv   rw   rx   ry   @py_assert4@py_assert7@py_assert6r   @py_format11s	            r2   "test_threshold_boundary_just_underz2TestFreshRecord.test_threshold_boundary_just_under~   s	   C5%ccIb"$EE.C	A!#3HEPPEPPPPEPPPPPP!PPP!PPPPPP#PPP#PPPPPP3PPP3PPPPPPHPPPHPPPEPPPPPPPPPPrO   Nrw   r   )__name__
__module____qualname____doc__r~   r   r   rI   rO   r2   rQ   rQ   d   s    H !QrO   rQ   c                  &    e Zd ZdZddZddZd Zy)TestStaleRecordu?   회귀 #2: 마지막 chairman record 가 90분 전이면 STALE.c                   d}|| dz  }t        |t        t        d      z
  t        |       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  }dd|iz  }t        t        j                  |            d x}}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  }dd|iz  }t        t        j                  |            d x}}d}|
|k(  }|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}}y )NSTALE001rT   Z   rU   r   rX   STALEr\   r^   r_   r`   rc   rd   re   Frf   rh   ri   rj   rk   ru   s              r2   test_classify_stalez#TestStaleRecord.test_classify_stale   s`   C5%ccIb$99CS"%	' #3CXFuuuu.sCXN%  x5    x5      x   x   5       uuuurO   c           	        d}|| dz  }t        |t        t        d      z
  t        |       t	        |t        |      }d}||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  t              rt        j                  t              nd
dt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)u?   정확히 60분 → STALE (≥ threshold 는 stale 로 박제).BNDRY060rT   <   rU   r   rX   r   r\   r   r   rx   rl   rw   r   r   r   Nr   r   s	            r2   %test_threshold_boundary_exactly_60minz5TestStaleRecord.test_threshold_boundary_exactly_60min   s   C5%ccIb$99CS"%	'!#3HEPPEPPPPEPPPPPP!PPP!PPPPPP#PPP#PPPPPP3PPP3PPPPPPHPPPHPPPEPPPPPPPPPPrO   c                p   d}t         |k(  }|st        j                  d|fdt         |f      dt        j                         v st        j
                  t               rt        j                  t               ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y)	u8   SCHEDULE_FRESHNESS_THRESHOLD_MIN 은 회장 §명시 60.r   r\   r^   r	   r`   rc   rd   N)	r	   rm   rn   ro   rp   rq   rr   rs   rt   rv   rz   r{   r|   r}   s        r2   test_constant_valuez#TestStaleRecord.test_constant_value   s_    355/25555/2555555/555/55525555555rO   Nr   )r   r   r   r   r   r   r   rI   rO   r2   r   r      s    I Q6rO   r   c                  (    e Zd ZdZddZddZddZy)TestMissingRecordu8   회귀 #3: schedule_history/{ID}.log 부재 → MISSING.c           	     z   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  }dd	|iz  }t        t        j                  |            d x}}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  }dd	|iz  }t        t        j                  |            d x}}d}|	|k(  }|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        |t        |      }
d }|
|u }|st        j                  d|fd|
|f      dt	        j
                         v st        j                  t              rt        j                  t              nddt	        j
                         v st        j                  |      rt        j                  |      nddt	        j
                         v st        j                  t              rt        j                  t              nddt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}
x}}y )NNONEXISTrX   MISSINGr\   r^   r_   r`   rc   rd   re   Frf   rh   ri   rj   )zR%(py5)s
{%(py5)s = %(py0)s(%(py1)s, now=%(py2)s, history_dir=%(py3)s)
} is %(py8)sr   rx   rl   rw   r   r   r   )r   rl   rm   rn   ro   rp   rq   rr   rs   rt   r   r   )rv   rw   rx   r_   rz   r{   r|   r}   ri   rj   r   r   r   r   r   s                  r2   %test_classify_missing_when_log_absentz7TestMissingRecord.test_classify_missing_when_log_absent   s   "3CXF!!u	!!!!u	!!!!!!u!!!u!!!	!!!!!!!.sCXN%  x5    x5      x   x   5       !!u	!!!!u	!!!!!!u!!!u!!!	!!!!!!!&sJRdRJdRRRRJdRRRRRR&RRR&RRRRRRsRRRsRRRRRRRRRRRRRRRRRRRRRJRRRdRRRRRRRrO   c           	        d}t        |t        |      }d}||k(  }|sSt        j                  d|fd||f      dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |      dt	        j
                         v st        j                  t              rt        j                  t              nddt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            d x}x}x}}y )N rX   r   r\   )zR%(py6)s
{%(py6)s = %(py0)s(%(py2)s, now=%(py3)s, history_dir=%(py4)s)
} == %(py9)sr   rl   rw   )ra   r   rb   r   r   py9zassert %(py11)spy11)
r   rl   rm   rn   ro   rp   rq   rr   rs   rt   )rv   rw   r{   r   @py_assert8r   @py_format10@py_format12s           r2   &test_empty_schedule_id_returns_missingz8TestMissingRecord.test_empty_schedule_id_returns_missing   s    "$Q!"#8DQ	QD	QQQQD	QQQQQQ!QQQ!QQQ"QQQQQQ#QQQ#QQQQQQ8QQQ8QQQDQQQ	QQQQQQQrO   c           	        d}|| dz  }t        |t        t        d      z
  d|       t        |t        |      }d}||k(  }|st	        j
                  d	|fd
||f      dt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  |      rt	        j                  |      nddt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}}y)u9   다른 chat 만 있는 log 는 MISSING (chairman 격리).OTHERCHTrT   r   rU    r   rX   r   r\   r   r   rx   rl   rw   r   r   r   Nr3   rl   r   r   rm   rn   ro   rp   rq   rr   rs   rt   r   s	            r2   ,test_log_with_no_chairman_records_is_missingz>TestMissingRecord.test_log_with_no_chairman_records_is_missing   s   C5%ccIb$997"%	'!#3HERRERRRRERRRRRR!RRR!RRRRRR#RRR#RRRRRR3RRR3RRRRRRHRRRHRRRERRRRRRRRRRrO   Nr   )r   r   r   r   r   r   r   rI   rO   r2   r   r      s    B	SRSrO   r   c                  .    e Zd ZdZd Zd Zd Zd Zd Zy) TestLifecycleStuckClassificationu   회귀 #4: lifecycle_reconciliation_manager.detect_stuck_cases 가
    STALE schedule_id + timer running → STALE_SCHEDULE_ID 박제.c                n   t        ddddd      }t        |      }|D ch c]  }|j                   }}t        j                  }||v }|st        j                  d|fd||f      d	t        j                         v st        j                  t              rt        j                  t              nd	t        j                  |      d
t        j                         v st        j                  |      rt        j                  |      nd
dz  }dd|iz  }t        t        j                  |            d x}}y c c}w )Nz	task-9501runningrS   r        @r5   r<   r    schedule_id_freshnessr   )in)z9%(py2)s
{%(py2)s = %(py0)s.STALE_SCHEDULE_ID
} in %(py4)sr   reasonsra   r   r   assert %(py6)sr   rN   r   reasonr   STALE_SCHEDULE_IDrm   rn   ro   rp   rq   rr   rs   rt   	rv   evcasescr   r{   r   @py_format5r   s	            r2   /test_stuck_case_emitted_for_stale_running_timerzPTestLifecycleStuckClassification.test_stuck_case_emitted_for_stale_running_timer   s    """)$*
 #2&%*+188++,,7,7777,777777{777{777,7777777777777777 ,   D2c                n   t        ddddd      }t        |      }|D ch c]  }|j                   }}t        j                  }||v}|st        j                  d|fd||f      d	t        j                         v st        j                  t              rt        j                  t              nd	t        j                  |      d
t        j                         v st        j                  |      rt        j                  |      nd
dz  }dd|iz  }t        t        j                  |            d x}}y c c}w )Nz	task-9502r   FRESH001r[   g     r@r   not inz=%(py2)s
{%(py2)s = %(py0)s.STALE_SCHEDULE_ID
} not in %(py4)sr   r   r   r   r   r   r   s	            r2   test_no_stuck_when_freshz9TestLifecycleStuckClassification.test_no_stuck_when_fresh   s    """)$)
 #2&%*+188++,,;,G;;;;,G;;;;;;{;;;{;;;,;;;;;;G;;;G;;;;;;; ,r   c                n   t        ddddd      }t        |      }|D ch c]  }|j                   }}t        j                  }||v}|st        j                  d|fd||f      d	t        j                         v st        j                  t              rt        j                  t              nd	t        j                  |      d
t        j                         v st        j                  |      rt        j                  |      nd
dz  }dd|iz  }t        t        j                  |            d x}}y c c}w )Nz	task-9503	completedr   r   r   r   r   r   r   r   r   r   r   r   r   s	            r2   $test_no_stuck_when_timer_not_runningzETestLifecycleStuckClassification.test_no_stuck_when_timer_not_running   s    $"")$*
 #2&%*+188++,,;,G;;;;,G;;;;;;{;;;{;;;,;;;;;;G;;;G;;;;;;; ,r   c           	     r   t        ddddddd      }t        |      }|D ch c]  }|j                   }}t        j                  }||v}|st        j                  d	|fd
||f      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            dx}}yc c}w )uU   PR MERGED 인 task 는 다른 stuck reason 이 처리 — STALE_SCHEDULE_ID 박제 X.z	task-9504MERGEDTr   STALE002r   r   )r5   r7   r9   r<   r    r   r   r   r   r   r   r   r   r   Nr   r   s	            r2   $test_no_stuck_when_pr_already_mergedzETestLifecycleStuckClassification.test_no_stuck_when_pr_already_merged   s    !""")$*
 #2&%*+188++,,;,G;;;;,G;;;;;;{;;;{;;;,;;;;;;G;;;G;;;;;;; ,s   D4c                n   t        ddddd      }t        |      }|D ch c]  }|j                   }}t        j                  }||v}|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      d	t        j                         v st        j                  |      rt        j                  |      nd	d
z  }dd|iz  }t        t        j                  |            dx}}yc c}w )u]   schedule_id_freshness=MISSING 은 보류 (stuck 박제 X — 매핑 부재만으로 stuck X).z	task-9505r   Nr   r   r   r   r   r   r   r   r   r   r   s	            r2   *test_missing_freshness_does_not_emit_stuckzKTestLifecycleStuckClassification.test_missing_freshness_does_not_emit_stuck  s    ""+$(
 #2&%*+188++,,;,G;;;;,G;;;;;;{;;;{;;;,;;;;;;G;;;G;;;;;;; ,r   N)	r   r   r   r   r   r   r   r   r   rI   rO   r2   r   r      s!    G
8
<
<<<rO   r   c                  .    e Zd ZdZd ZddZddZddZy)TestChairmanChatIsolationuR   회귀 #5: chat=6937032012 외 record 는 freshness 판정에 포함되지 않음.c                p   d}t         |k(  }|st        j                  d|fdt         |f      dt        j                         v st        j
                  t               rt        j                  t               ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}}y )Nl   L5: r\   r^   r
   r`   rc   rd   )	r
   rm   rn   ro   rp   rq   rr   rs   rt   r   s        r2   test_chairman_chat_id_constantz8TestChairmanChatIsolation.test_chairman_chat_id_constant  s_    #--:----:------------:-------rO   c                <   d}|| dz  }t        |t        t        d      z
  d|       t        |t        t        d      z
  t        |       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  동일 schedule_id 에 다른 chat 의 fresh record 가 있어도 chairman record 가
        STALE 이면 STALE 로 박제 (다른 chat record 는 격리되어 freshness 판정 미참여).

        시나리오:
          - schedule_id 같은 log 파일에
          - chairman chat (6937032012) 의 90분 전 record
          - 다른 chat (9999999) 의 5분 전 record (포함되면 안 됨)
          → 기대: STALE (chairman 만 보면 90분 전 → STALE)
        ISOL0001rT   r   rU   r   r   r   rX   r   r\   r^   r_   r`   uC   다른 chat record 가 포함되면 안 됨 — chairman 만 사용z
>assert %(py5)srd   N)r3   rl   r   r
   r   rm   rn   ro   rp   rq   rr   _format_assertmsgrs   rt   )	rv   rw   rx   ry   r_   rz   r{   r|   r}   s	            r2   test_other_chat_records_ignoredz9TestChairmanChatIsolation.test_other_chat_records_ignored  s     C5%ccIa$88'"%	' 	ccIb$99CS"%	' #3CXFfufffuffffffufffuffffff!fffffffrO   c           	        d}|| dz  }dD ]"  }t        |t        t        |      z
  d|       $ t        |t        |      }d}||k(  }|st	        j
                  d	|fd
||f      dt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  |      rt	        j                  |      nddt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            d x}x}}y )NISOL0002rT   )r   r         (   rU   r   r   rX   r   r\   r   r   rx   rl   rw   r   r   r   r   )
rv   rw   rx   ry   offsetr   r   r   r   r   s
             r2   *test_only_other_chat_records_yield_missingzDTestChairmanChatIsolation.test_only_other_chat_records_yield_missing/  s   C5%) 	+F##	&(A"A7&)+	+
 "#3HERRERRRRERRRRRR!RRR!RRRRRR#RRR#RRRRRR3RRR3RRRRRRHRRRHRRRERRRRRRRRRRrO   c           	        d}|| dz  }t         t        d      z
  j                         d|dddd}|j                  t	        j
                  |d	
      dz   d       t        |t         |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  t               rt        j                  t               nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y)uW   chat_id 가 문자열 '6937032012' 로 직렬화돼도 정상 인식 (cokacdir 호환).STR_CHIDrT   r   rU   
6937032012r   r   )r   r   r    r   r   r   Fr'   r)   r$   r%   rX   r[   r\   r   r   rx   rl   rw   r   r   r   N)rl   r   r*   
write_textr-   r.   r   rm   rn   ro   rp   rq   rr   rs   rt   )
rv   rw   rx   ry   r0   r   r   r   r   r   s
             r2   +test_chairman_record_string_chat_id_handledzETestChairmanChatIsolation.test_chairman_record_string_chat_id_handled:  s9   C5%2..99;#
 	tzz&u=DwW!#3HEPPEPPPPEPPPPPP!PPP!PPPPPP#PPP#PPPPPP3PPP3PPPPPPHPPPHPPPEPPPPPPPPPPrO   Nr   )r   r   r   r   r   r   r   r   rI   rO   r2   r   r     s    \.g,	SQrO   r   c                  $    e Zd ZdZddZd Zd Zy)TestTokenRawZerouE   회귀 #6: validator 출력 / stuck detail 에 token raw prefix 0건.c                   d}|| dz  }t        |t        t        d      z
  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  }dd|iz  }t        t        j                  |            dx}}t        D ]  }	|	|v}|st        j                  d|fd|	|f      dt        j                         v st        j                  |	      rt        j                  |	      nddt        j                         v st        j                  |      rt        j                  |      nddz  }
dd|
iz  }t        t        j                  |            d} y)ua   log 의 response 본문에 token 이 있어도 freshness 판정 결과에는 노출되지 않음.TOKEN001rT   r   rU   z-this should not leak: ghs_FAKE_TOKEN_ABCDEFGHrW   rX   r[   r\   r^   r_   r`   rc   rd   Nr   z%(py0)s not in %(py2)sprefixra   r   zassert %(py4)sr   )r3   rl   r   r
   r   rm   rn   ro   rp   rq   rr   rs   rt   TOKEN_PREFIXES)rv   rw   rx   ry   r_   rz   r{   r|   r}   r  @py_format3r   s               r2   3test_classify_freshness_does_not_leak_response_bodyzDTestTokenRawZero.test_classify_freshness_does_not_leak_response_bodyQ  s   C5%ccIb$99CS"%N	P #3CXFuuuu$ 	'F&&&&6&&&&&&6&&&6&&&&&&&&&&&&&&&&	'rO   c                   t        ddddd      }t        |      }|D cg c]"  }|j                  t        j                  k(  s!|$ }}t        |      }d}||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  t
              rt        j                  t
              nd
dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            d x}x}}|d   j                  }
t        D ]  }||
v}|st        j                  d|fd||
f      dt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  |
      rt        j                  |
      nddz  }t        j                   d| d      dz   d|iz  }t        t        j                  |            d } y c c}w )Nz	task-2535r   rS   r   r   r      r\   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenstale_cases)ra   r   rb   r   r   r   r   r   r  r  detailr  ztoken prefix z leaked in detail
>assert %(py4)sr   )rN   r   r   r   r   r
  rm   rn   ro   rp   rq   rr   rs   rt   r  r  r   )rv   r   r   r   r  rz   r   r   r   r   r  r  r{   r  r   s                  r2   *test_stuck_detail_contains_no_token_prefixz;TestTokenRawZero.test_stuck_detail_contains_no_token_prefix`  s~   """)$*
 #2&"'UQ188{7T7T+TqUU;$1$1$$$$1$$$$$$s$$$s$$$$$$;$$$;$$$$$$1$$$$$$$Q&&$ 	SF'RRR6RRRRRR6RRR6RRRRRRRRRRRRR=@Q)RRRRRRR	S Vs   "IIc                <   t         dz  dz  }|j                  d      }t        D ]  }||v}|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  |      rt        j                  |      ndd	z  }t        j                  | d
|j                         dz   d|iz  }t        t        j                  |            d} y)u]   모듈 소스 자체에 token raw prefix 가 없는지 회귀 (회장 §금지 token 박제).utilszschedule_id_freshness.pyr$   r%   r   r  r  textr  z
 found in r  r   N)_WORKTREE_ROOT	read_textr  rm   rn   ro   rp   rq   rr   r   namers   rt   )rv   src_pathr  r  r{   r  r   s          r2   #test_module_source_no_token_stringsz4TestTokenRawZero.test_module_source_no_token_stringso  s    !G+.HH!!7!3$ 	LF%KKK6KKKKKK6KKK6KKKKKKKKKKKKK&HMM?'KKKKKKK	LrO   Nr   )r   r   r   r   r  r  r  rI   rO   r2   r   r   N  s    O'SLrO   r   __main__z-v)r/   r   r   r   r   intr    strr   r  r   r  r   r  returnNone)rL   r   r  r   )3r   
__future__r   builtinsro   _pytest.assertion.rewrite	assertionrewriterm   r-   sysr   r   r   pathlibr   typingr   pytest__file__resolveparentr  r  r/   removeinsertutils.schedule_id_freshnessr	   r
   r   r   r   &utils.lifecycle_reconciliation_managerr   r   r   utcrl   r  r3   rN   rQ   r   r   r   r   r   r   exitmainrI   rO   r2   <module>r/     si   #    
 2 2   h'')0077>>~#(("HHOOC'( 3~& '   tQB1X\\:0 !%d#??03?DG?RV? %> Q  QN6 6BS S<B< B<R4Q 4Qv&L &LR zCHH[V[[(D)*+ rO   