
    ai-]                    0   d Z ddlmZ ddlZddlmc mZ ddl	Z	ddl
Z
ddlZddlZddlZddlZddlZddlZddlZddlmZmZ ddlmZ ddlmZ ddlmZ  ee      j9                         j:                  j:                  j:                  Z ee      ej@                  v r!ej@                  jC                   ee             ej@                  jE                  d ee             edz  d	z  Z# ee#      ej@                  vr"ej@                  jE                  d ee#             ddl$Z$ddl%Z%e#d
z  Z&e#dz  Z'e#dz  Z( ee      j9                         Z)ddd	 d dZ*d!dZ+d"dZ,d"dZ-d!dZ.d#dZ/d$dZ0d%dZ1d$dZ2d%dZ3d&dZ4d'dZ5d$dZ6d$dZ7d%dZ8d$dZ9d$dZ:d$dZ;y)(u  tests/regression/test_dashboard_readonly_2524.py

회귀 테스트 — task-2524 automation observability dashboard v0 (read-only).

회장 §명시 7 검증 (정확히 7개):
  1. mutation 정적 검사 PASS
     (gh pr merge / cokacdir --cron / --apply / mutation HTTP 핸들러 부재)
  2. chat=6937032012만 표시 (multi-tenant 격리)
  3. token raw value 0 노출 (ghs_/ghp_/github_pat_ prefix 부재)
  4. 7 source 정합 (task-timer / schedule_history / PR / CI+Gemini /
     lifecycle / merge audit / Critical 7종)
  5. HTTP server POST/PUT/DELETE 거부 (405 Method Not Allowed)
  6. port 충돌 회피 (기존 8000과 다른 port; 권장 8001)
  7. task-2526 영역 (scripts/safe_cron_dispatch.py 등) 무수정

회장 §보안:
  - gh CLI 실호출 X — runner 인자로 dataclass-mock 주입.
  - HTTP 서버는 단일 요청 (handle_request) 만 띄워서 검증 후 즉시 close.
  - bind 는 항상 loopback (127.0.0.1). 외부 노출 시도 0건.
  - 어떠한 mutation tool / dispatch.py / cron CLI 도 import / 호출 X.
    )annotationsN)datetimetimezone)HTTPConnection)Path)Anytoolsobservabilitydashboard_aggregator.pydashboard_server.pydashboard_view.htmlprschecksc                6      xs g  xs i ddd fd}|S )Ng      (@timeoutc               D   ~	 | j                  d      }|dz   t        |       k  r| |dz      nd}|dk(  rdt        j                        dfS |dk(  r9t        | |dz            }dt        j                  j                  |g             dfS ddd	| fS # t        $ r Y yw xY w)
Npr)    zno pr subcommandr   r   listr   r      zunknown gh subcommand: )index
ValueErrorlenjsondumpsintget)argsr   sub_idxsubnumberr   r   s        ^/home/jay/workspace/.worktrees/task-2524-dev5/tests/regression/test_dashboard_readonly_2524.py_runnerz_make_runner.<locals>._runnerJ   s    	-jj&G $+Q;T#:d7Q;&=djjor))(?gk*+FdjjFB!78"<<"/u555  	-,	-s   B 	BB)r!   z	list[str]r   floatreturnztuple[int, str, str] )r   r   r&   s   `` r%   _make_runnerr*   E   s)    
)C\rF59 6 6 N    c                v    | j                  t        j                  dddddddddd	d
dddddi      d       y )Ntasks	task-2524z	dev5-teamrunningz2026-05-10T10:00:00zobservability dashboard v0)task_idteam_idstatus
start_timedescription	task-2523	completedz2026-05-09T08:00:00z2026-05-09T09:30:00u   1시간 30분zbot merge identity regression)r0   r1   r2   r3   end_timeduration_humanr4   )r.   r5   utf-8encoding
write_textr   r   )paths    r%   _write_task_timersr?   `   s`    OODJJ&&#3; '&%31"1>
  & '  r+   c                    | dz  }|j                  t        j                  dddddddd      d	z   d
       | dz  }|j                  t        j                  dddddddd      d	z   d
       y)uT   1 ok record (chat=6937032012) + 1 alien record (chat=99999) — 격리 테스트용.zOWN1234.logOWN1234   L5: z2026-05-10T08:00:00+09:00oki90  ztask-2524 dashboard build)schedule_idchat_idtsr2   duration_mspromptresponse
r9   r:   zALIEN999.logALIEN999i z2026-05-10T08:01:00+09:00d   u    OTHER TENANT — must not appearzleaked?Nr<   )dir_own_log	alien_logs      r%   _write_schedule_historyrP   w   s    ]"Gtzz )-#  
    ! ~%I!)4%  
    !r+   c                >    dD ]  }| |z  j                  dd        y )N)ztask-2523.done.ackedztask-2523.done.escalatedztask-2523.done.mergedztask-2523.done.notifiedztask-2522.done.clearztask-2522.merge-doner   r9   r:   )r=   )rM   names     r%   _write_lifecycle_markersrS      s,     7 
  g 67r+   c                    | j                   j                  dd       t        j                  dddddddd	      t        j                  d
ddddddd	      g}| j	                  dj                  |      dz   d       y )NTparentsexist_ok2026-05-09T09:01:27ZI   app/jeon-jonghyuk-taskctl-botGITHUB_APP_INSTALLATION_TOKENFBOT_MERGE_IDENTITY_SUCCESS)rF   r   mergedBytoken_sourceowner_pat_usedexpected_bot_identityclassificationz2026-05-08T11:22:00ZH   JonghyukJeon	OWNER_PATOWNER_PAT_FALLBACKrJ   r9   r:   )parentmkdirr   r   r=   join)r>   liness     r%   _write_merge_auditrj      s    KKdT2

(7;#%D:
 	 	

(&'"%D2
 	E( 	OODIIe$t+gO>r+   c                   | j                  dd       |j                         }|j                  |j                  dz
        j                         }| dz  }t	        j
                  |ddd      t	        j
                  |d	d
d      t	        j
                  |ddd      t	        j
                  |dd      g}|j                  dj                  |      dz   d       y)uP   Critical 7종 중 2건을 24h 윈도우 안에, 1건을 윈도우 밖에 박제.TrU   r   )yearzcritical-incidents.jsonlFORBIDDEN_PATH_INTRUSIONztask-XYZ)rF   escalation_typer0   POST_MERGE_SMOKE_FAILUREztask-ABC)rF   typer0   REPLACEMENT_PR_FAILEDztask-OLDNOT_A_CRITICAL)rF   rn   rJ   r9   r:   N)rg   	isoformatreplacerl   r   r   r=   rh   )rM   now
inside_isooutside_isof1ri   s         r%   _write_critical_auditry      s    JJtdJ+J;;CHHqL;1<<>K	*	*B

9!
 	
 	

.!
 	
 	

6!
 	
 	

*9IJK!E$ MM$))E"T)GM<r+   c            
        d} t        j                  d      }t        j                  d      }t        t        fD ]+  }|j	                  d      }|j
                  } ||      }| }|st        j                  d|j                         dz   dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
z  }t        t        j                  |            d x}x}}| 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|j                         dz   d|
iz  }t        t        j                  |            d } |j
                  } ||      }| }|st        j                  d|j                         dz   dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
z  }t        t        j                  |            d x}x}}dD ]  }||v}|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndd	t        j                         v st        j                  |      rt        j                  |      nd	dz  }
t        j                  d|d|j                         dz   d|
iz  }t        t        j                  |            d } . t         j	                  d      }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                  |      t        j                  |      dz  }t        j                  d      dz   d|iz  }t        t        j                  |            d x}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                  |      t        j                  |      dz  }t        j                  d       dz   d|iz  }t        t        j                  |            d x}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                  |      t        j                  |      dz  }t        j                  d       dz   d|iz  }t        t        j                  |            d x}x}x}}d"D ]j  }	|	j"                  } |       }|j"                  } |       }||v}|s0t        j                  d|fd#||f      dt        j                         v st        j                  |	      rt        j                  |	      ndt        j                  |      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d$z  }t        j                  d%|	d&      d'z   d(|iz  }t        t        j                  |            d x}x}x}x}}m y ))N)zgh pr mergezcokacdir --cronzcokacdir --applyzcokacdir --sendfilez(lifecycle_reconciliation_manager --applyzgit pushz
git commitzgit reset --hardz
git rebasezshutil.rmtree(z
os.remove(z
os.unlink(zopen\([^)]*['\"][wax][b+]?['\"]z$\bdef\s+do_(POST|PUT|PATCH|DELETE)\br9   r:   z mutation HTTP method handler in zN
>assert not %(py5)s
{%(py5)s = %(py2)s
{%(py2)s = %(py0)s.search
}(%(py3)s)
}method_handler_retextpy0py2py3py5not inz%(py0)s not in %(py2)sneedler~   r   zforbidden mutation token z in 
>assert %(py4)spy4zwrite-mode open() in write_modes_re)zfrom utils import dispatchzfrom utils.dispatchzyaml.safe_dumpforbidden_importzforbidden import z<form)zH%(py1)s not in %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = %(py3)s.lower
}()
}	view_textpy1r   r   py7z*dashboard_view.html must not contain formsz
>assert %(py9)spy9zmethod="post"z!dashboard_view.html must not POSTzmethod='post')zmethod:"POST"zmethod: "POST"zmethod:"PUT"zmethod: "PUT"zmethod:"DELETE"zmethod: "DELETE")z|%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.lower
}()
} not in %(py10)s
{%(py10)s = %(py8)s
{%(py8)s = %(py6)s.lower
}()
})r~   r   r   py6py8py10zmutation fetch z in dashboard_view.htmlz
>assert %(py12)spy12)recompile_AGGREGATOR_PATH_SERVER_PATH	read_textsearch
@pytest_ar_format_assertmsgrR   @py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanation_call_reprcompare
_VIEW_PATHlower)forbidden_substringsr   r{   src_pathr|   @py_assert1@py_assert4@py_assert6@py_format7r   @py_format3@py_format5r   r   @py_assert0@py_assert2@py_format8@py_format10@py_assert3@py_assert7@py_assert9@py_assert5@py_format11@py_format13s                           r%   3test_v1_no_mutation_in_aggregator_or_view_or_serverr      sz   - ZZ BCN

#JK%|4 m!!7!3$++e+D1e11e1ee5UV^VcVcUd3eeeeeee$eee$eee+eeeeeeDeeeDeee1eeeeee* 	aF%```6``````6```6`````````````)B6*DQYQ^Q^P_'```````	a "((W(.W..W.WW2G0WWWWWWW>WWW>WWW(WWWWWWWWWWWW.WWWWWW g 	m#4/lll#4llllll#lll#llllll4lll4llll3DEUDXX\]e]j]j\k1lllllll	mm $$g$6IY)//Y/+Y7++YYY7+YYY7YYYYYY)YYY)YYY/YYY+YYY-YYYYYYYYZIOOZO$5Z$55ZZZ$5ZZZZZZZZZIZZZIZZZOZZZ$5ZZZ7ZZZZZZZZX)//X/"3X?"33XXX?"3XXX?XXXXXX)XXX)XXX/XXX"3XXX5XXXXXXXX: l||k|~kY__k_%6k~%66kkk~%6kkkkkkvkkkvkkk|kkk~kkkkkkYkkkYkkk_kkk%6kkk/&Sj8kkkkkkkklr+   c                R	   | dz  }|j                          t        |       t        j                  |d      }|d   }d}||u }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }d	d
|iz  }t        t	        j                  |            d x}x}}|d   }d}||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 cg c]  }|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}}|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}}t        j                  |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  }
d$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  }
d$d|
iz  }t        t	        j                  |            d x}}y c c}w )&Nschedule_historyrB   )history_dirallowed_chat_id	availableTisz%(py1)s is %(py4)sr   r   assert %(py6)sr   r   ==z%(py1)s == %(py4)srecordsrD   rA   inz%(py1)s in %(py3)sidsr   r   zown-tenant record must appear
>assert %(py5)sr   rK   r   )z%(py1)s not in %(py3)sz#other tenant record must NOT appearskipped_other_tenantsr   >=)z%(py1)s >= %(py4)sFensure_asciizOTHER TENANTblobassert %(py5)sleaked)rg   rP   dashboard_aggregatorcollect_schedule_historyr   r   r   r   r   r   r   r   r   r   r   )tmp_pathr   resultr   r   r   r   r   rr   @py_format4@py_format6r   s                r%   .test_v2_schedule_history_filters_other_tenantsr     s   //KK(!::"F +&$&$&&&&$&&&&&&$&&&&&&&#$2
2$
2222$
222$222
2222222%+I%6
71]
7C
7<9<<<9<<<9<<<<<<<<<<<<<<<<<<<<G:S GGG:SGGG:GGGGGGSGGGSGGGG"GGGGGGG)*/a/*a////*a///*///a///////::f51D%>%%%%>%%%>%%%%%%%%%%%%%%%%8484844 8s   >R$c                    ddddgddid} t        j                  |       }t        j                  |      }t         j                  D ]  }||v}|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                  d|d      dz   d|iz  }t        t        j                  |            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}}t        j                  d      t!        fdt         j                  D              }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 )!NTz@merging with token ghs_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA done/github_pat_BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB(ghp_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCtoken(ghs_DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD)rC   logextranestedr   r   prefixr   r   zraw token prefix z leaked into redacted outputr   r   z[REDACTED_TOKEN]r   r   r   z$redaction marker must replace tokensr   r   r9   r:   c              3  @   K   | ]  }j                  |        y wN)count).0paggregator_texts     r%   	<genexpr>zMtest_v3_token_raw_value_zero_exposure_in_aggregator_output.<locals>.<genexpr>+  s     c!_2215cs      r   z%(py0)s == %(py3)sprefix_occurrencesr~   r   z3unexpected token prefix occurrences in aggregator: )r   _redact_token_stringsr   r   TOKEN_PREFIXESr   r   r   r   r   r   r   r   r   r   r   sum)poisoned_payloadredactedr   r   r   r   r   r   r   r   r   r   r   s               @r%   :test_v3_token_raw_value_zero_exposure_in_aggregator_outputr     s   QCEopFG	 $99:JKH::hD&55 ^T!]]]vT]]]]]]v]]]v]]]]]]T]]]T]]]]%6vj@\#]]]]]]]^M%MMMMMMMMMMMMMMMMMMM'MMMMMMM '00'0BOc?S?b?bcc!" "                "#    >>P=QR    r+   c                r   | dz  }| dz  }| dz  }| dz  }|j                          |j                          t        |       t        |       t        |       |dz  }t	        |       t        dddd	d
d
t        j                        }t        ||       t        dddddidddidddidd	dddd d d dddidd	gdddd d!ddd gdddd d!d d"d gd#$      }t        j                  ||||||%      }|d&   }	|	j                  }
d'} |
|      }|st        j                  d(      d)z   t        j                  |	      t        j                  |
      t        j                  |      t        j                  |      d*z  }t!        t        j"                  |            d x}	x}
x}}|d+   }h d,}t%        |      }
|
|k(  }|sEt        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                  |
      d0t)        j*                         v st        j,                  |      rt        j                  |      nd0d1z  }t        j                  d2t%        |             d3z   d4|iz  }t!        t        j"                  |            d x}
}|d5   d6   }	d7}|	|u }
|
slt        j&                  d8|
fd9|	|f      t        j                  |	      t        j                  |      d:z  }d;d<|iz  }t!        t        j"                  |            d x}	x}
}d= |d5   d>   D        }t/        |      }|sd?d@t)        j*                         v st        j,                  t.              rt        j                  t.              nd@t        j                  |      t        j                  |      dAz  }t!        t        j"                  |            d x}}|d   dB   }	t        j0                  }|	|k(  }
|
st        j&                  d-|
fdC|	|f      t        j                  |	      dDt)        j*                         v st        j,                  t              rt        j                  t              ndDt        j                  |      dEz  }dFd4|iz  }t!        t        j"                  |            d x}	x}
}|dG   d6   }	d7}|	|u }
|
slt        j&                  d8|
fd9|	|f      t        j                  |	      t        j                  |      d:z  }d;d<|iz  }t!        t        j"                  |            d x}	x}
}|dG   dH   }	dI}|	|k(  }
|
slt        j&                  d-|
fdJ|	|f      t        j                  |	      t        j                  |      d:z  }d;d<|iz  }t!        t        j"                  |            d x}	x}
}|dK   dL   D ci c]  }|dM   |dN    }}|d   dO   }	d7}|	|u }
|
slt        j&                  d8|
fd9|	|f      t        j                  |	      t        j                  |      d:z  }d;d<|iz  }t!        t        j"                  |            d x}	x}
}|d   dP   }	dI}|	|k(  }
|
slt        j&                  d-|
fdJ|	|f      t        j                  |	      t        j                  |      d:z  }d;d<|iz  }t!        t        j"                  |            d x}	x}
}|d   dQ   }	dR}|	|k(  }
|
slt        j&                  d-|
fdJ|	|f      t        j                  |	      t        j                  |      d:z  }d;d<|iz  }t!        t        j"                  |            d x}	x}
}|dS   dT   }|j2                  }dU}d
} |||      }dR}||k\  }|st        j&                  dV|fdW||f      dTt)        j*                         v st        j,                  |      rt        j                  |      ndTt        j                  |      t        j                  |      t        j                  |      t        j                  |      t        j                  |      dXz  }dYdZ|iz  }t!        t        j"                  |            d x}x}x}x}x}}|j2                  }d[}d
} |||      }dR}||k\  }|st        j&                  dV|fdW||f      dTt)        j*                         v st        j,                  |      rt        j                  |      ndTt        j                  |      t        j                  |      t        j                  |      t        j                  |      t        j                  |      dXz  }dYdZ|iz  }t!        t        j"                  |            d x}x}x}x}x}}|d\   d]   }	dR}|	|k(  }
|
slt        j&                  d-|
fdJ|	|f      t        j                  |	      t        j                  |      d:z  }d;d<|iz  }t!        t        j"                  |            d x}	x}
}|d\   d^   }	dR}|	|k(  }
|
slt        j&                  d-|
fdJ|	|f      t        j                  |	      t        j                  |      d:z  }d;d<|iz  }t!        t        j"                  |            d x}	x}
}|d_   }|d6   }	d7}|	|u }
|
slt        j&                  d8|
fd9|	|f      t        j                  |	      t        j                  |      d:z  }d;d<|iz  }t!        t        j"                  |            d x}	x}
}|d`   }t%        |      }t        j4                  }t%        |      }||k(  }|sot        j&                  d-|fda||f      d/t)        j*                         v st        j,                  t$              rt        j                  t$              nd/t        j                  |      t        j                  |      d/t)        j*                         v st        j,                  t$              rt        j                  t$              nd/dDt)        j*                         v st        j,                  t              rt        j                  t              ndDt        j                  |      t        j                  |      dbz  }dYdZ|iz  }t!        t        j"                  |            d x}x}x}x}}|dc   dd   }	dR}|	|k(  }
|
slt        j&                  d-|
fdJ|	|f      t        j                  |	      t        j                  |      d:z  }d;d<|iz  }t!        t        j"                  |            d x}	x}
}|dc   de   }	dR}|	|k(  }
|
slt        j&                  d-|
fdJ|	|f      t        j                  |	      t        j                  |      d:z  }d;d<|iz  }t!        t        j"                  |            d x}	x}
}|dc   df   }	d
}|	|k(  }
|
slt        j&                  d-|
fdJ|	|f      t        j                  |	      t        j                  |      d:z  }d;d<|iz  }t!        t        j"                  |            d x}	x}
}y c c}w )gNtask-timers.jsonr   eventszorchestration-auditzmerge-queue.jsonli     
      r   )tzinfo)ru   rY   MERGEDztask-2523 regressionloginrZ   rX   oid(b7a37521c8f189bfd98b8ea047ebaa5c5fe684aaztask/task-2523-dev5ztask-bot)	r$   statetitler]   mergedAtmergeCommitheadRefNameauthor	updatedAtJ   OPENztask-2524 dashboardztask/task-2524-dev5z2026-05-10T08:00:00Zzci/buildSUCCESS	COMPLETED)rR   
conclusionr2   zgemini-reviewIN_PROGRESS)rY   r  r   )timers_pathr   
events_dirmerge_audit_path	audit_dirrunnerschema_versionr.   z"schema_version must mark task-2524zN
>assert %(py7)s
{%(py7)s = %(py3)s
{%(py3)s = %(py1)s.startswith
}(%(py5)s)
}r   sources>   pr_state	ci_gemini
critical_7
task_timermerge_auditr   lifecycle_markersr   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py5)ssetexpected_keys)r~   r   r   r   z7 source keys mismatch: 
>assert %(py7)sr   r  r   Tr   r   r   r   r   c              3  ,   K   | ]  }|d    dk(    yw)r0   r.   Nr)   )r   r   s     r%   r   z>test_v4_seven_sources_present_and_populated.<locals>.<genexpr>r  s     Uqq|{*Us   r/   z,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}anyr~   r   r   r   )z7%(py1)s == %(py5)s
{%(py5)s = %(py3)s.ALLOWED_CHAT_ID
}r   )r   r   r   assert %(py7)sr  totalr   r   r  per_prr   summarygemini_presentsuccesspendingr   r  countsz
done.ackedr   )zS%(py8)s
{%(py8)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s, %(py6)s)
} >= %(py11)s)r~   r   r   r   r   py11zassert %(py13)spy13z
merge-doner   bot_merge_identity_success_countowner_pat_used_countr  canonical_types)zz%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py11)s
{%(py11)s = %(py6)s(%(py9)s
{%(py9)s = %(py7)s.CRITICAL_7_CANONICAL
})
})r~   r   r   r   r   r   r)  by_typerm   POST_MERGE_SMOKE_FAILEDrq   )rg   r?   rP   rS   rj   r   r   utcry   r*   r   	aggregate
startswithr   r   r   r   r   r  r   r   r   r   r  ALLOWED_CHAT_IDr    CRITICAL_7_CANONICAL) r   r  r   r  r  
merge_pathru   r  payloadr   r   r   r   r   r  r  r   r   r   r   r   entryci_pr_summariesr(  r   r   @py_assert10r   @py_format12@py_format14crit@py_assert8s                                    r%   +test_v4_seven_sources_present_and_populatedr>  6  s
   //K//KH$J00I{#K(Z(00Jz"
4BAq
>C)-H7M!#BC/"$NO1gz=R02 F5J41gz=R02
 $9T(	[Y
 $9T(V	
F2 #,,#G #$b$//bb/<b<bb>bbbb$bbb/bbbbbb<bbbbbbi GM w<S<=(SSS<=SSSSSS3SSS3SSSSSSwSSSwSSS<SSSSSS=SSS=SSSS,DS\N*SSSSSSS < -55-5555-555-5555555555UGL4I)4TUU3UUUUUUUUU3UUU3UUUUUUUUUUUUUU%&'89a=Q=a=aa9=aaaaa9=aaaa9aaaaaa=Qaaa=Qaaa=aaaaaaaa:{+3t3+t3333+t333+333t3333333:w',1,'1,,,,'1,,,',,,1,,,,,,,BI+BVW_B`auT{E)$44aOa2/08D80D88880D8880888D88888882y).Q.)Q....)Q...)...Q.......2y).Q.)Q....)Q...)...Q.......()(3F::+l+A+:lA&+!+&!++++&!++++++6+++6+++:+++l+++A+++&+++!++++++++::+l+A+:lA&+!+&!++++&!++++++6+++6+++:+++l+++A+++&+++!++++++++=!"DEJJEJJJJEJJJEJJJJJJJJJJ=!"89>Q>9Q>>>>9Q>>>9>>>Q>>>>>>>< D$$$$$$$$$$$$$$$$$$$%&Y3&'Y/C/X/XY3/X+YY'+YYYYY'+YYYYYYY3YYY3YYY&YYY'YYYYYY3YYY3YYYYYY/CYYY/CYYY/XYYY+YYYYYYYY	?56;!;6!;;;;6!;;;6;;;!;;;;;;;	?45::5::::5:::5::::::::::	?238q83q88883q8883888q8888888' bs   $z4c                     t        j                   t         j                  t         j                        5 } | j                  d       | j	                         d   cd d d        S # 1 sw Y   y xY w)N)	127.0.0.1r   r   )socketAF_INETSOCK_STREAMbindgetsockname)socks    r%   
_free_portrG    sO    	v~~v'9'9	: %d		"#!!$% % %s   $A!!A*c                R   t        j                  d|       }t        j                  |j                  d      }|j                          t        j                         dz   }t        j                         |k  rd	 t        j                  j                  d|  dd	      5 }|j                  d
k(  r	 d d d        ||fS 	 d d d        t        j                         |k  rd||fS # 1 sw Y   %xY w# t        j                  j                  t        f$ r t        j                  d       Y bw xY w)Nr@  hostportT)targetdaemong      @zhttp://127.0.0.1:z/healthzg      ?r      g?)dashboard_serverbuild_server	threadingThreadserve_foreverstarttimeurllibrequesturlopenr2   errorURLErrorConnectionRefusedErrorsleep)rK  serverthreaddeadlineresps        r%   _serve_in_backgroundra    s    **$GFV%9%9$GF
LLNyy{S H
))+
 	''*;D6(JTW'X \`;;#%
 6>	 & ))+
  6>  %%'=> 	JJt	s0   8%C+ C.C+ ;C+ C($C+ +8D&%D&c                 	   t               } t        |       \  }}	 dD ]%  }t        d| d      }	 |j                  |ddddi	       |j	                         }|j                         j                  d
d      }|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}	}|j#                  d      xs d}g }d}||v }|}|rd}||v }|}|sXt        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }
dd|
iz  }|j%                  |       |rt        j                  dfd|f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }d d!|iz  }|j%                  |       t        j&                  |d"      i z  }d#d$|iz  }t        t        j                   |            d x}x}x}x}x}}t)        j*                  |      }|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+   }||k(  }|st        j                  d|fd,||f      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}}|j-                          ( 	 |j/                          |j1                          |j3                  d       y # |j-                          w xY w# |j/                          |j1                          |j3                  d       w xY w)0N)POSTPUTDELETEPATCHr@  g       @r   z/datas   {}zContent-Typezapplication/json)bodyheadersr9   rt   )errorsi  r   )z.%(py2)s
{%(py2)s = %(py0)s.status
} == %(py5)sr`  r~   r   r   z should be 405, got r  r   Allowr   GETHEADr   )z%(py3)s in %(py5)sallow_header)r   r   z%(py7)s)z%(py10)s in %(py12)s)r   r   z%(py14)spy14r   zassert %(py17)spy17rY  zmethod not allowedr   r   r   r   method)z%(py1)s == %(py3)sr   r   r   )rG  ra  r   rW  getresponsereaddecoder2   r   r   r   r   r   r   r   r   r   	getheaderappend_format_boolopr   loadscloseshutdownserver_closerh   )rK  r]  r^  rq  connr`  rg  r   r   r   r   r   rn  r   r   r   @py_assert11r   @py_format15@py_format16@py_format18r6  r   r   r   s                            r%   6test_v5_mutation_methods_return_405_method_not_allowedr    s   <D)$/NFF!8 	F!+tSADVW5>SeBfg'')yy{))'))D{{WcW{c)WWW{cWWWWWWtWWWtWWW{WWWcWWWfX5I$+++WWWWWWWW#~~g6<"GuGu,GG<1GGGGGuGGGuGGGGGGGGGGGGGGGG<GGGGGGGGG<GGG<GGGGGGGGGGGGGG**T*w'?+??'+?????'+????'???+????????x(2(F2222(F222(222222F222F2222222

	 	C 	 

C s#   R PQ92R 9RR 4Sc                 
   t         j                  } d}| |k7  }|st        j                  d|fd| |f      dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |       t        j                  |      dz  }dd|iz  }t        t        j                  |            d x} x}}t         j                  } d}| |k(  }|st        j                  d	|fd
| |f      dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |       t        j                  |      dz  }dd|iz  }t        t        j                  |            d x} x}}t         j                  } ddh}| |v }|st        j                  d|fd| |f      dt	        j
                         v st        j                  t               rt        j                  t               ndt        j                  |       t        j                  |      dz  }dd|iz  }t        t        j                  |            d x} x}}	 t        j                  dt                      t        d      # t        $ rY}g } d}t        |      }||v }|}|sd}	t        |      }
|	|
v }|}|st        j                  d|fd||f      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                  |      dz  }dd|iz  }| j                  |       |st        j                  dfd	
f      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                  |
      dz  }dd|iz  }| j                  |       t        j                   | d      i z  }dd|iz  }t        t        j                  |            d x}x} x}x}x}x}	x}}
Y d }~y d }~ww xY w) Ni@  )!=)z4%(py2)s
{%(py2)s = %(py0)s.DEFAULT_PORT
} != %(py5)srO  rj  r!  r   iA  r   )z4%(py2)s
{%(py2)s = %(py0)s.DEFAULT_PORT
} == %(py5)sr@  	localhostr   )z4%(py2)s
{%(py2)s = %(py0)s.DEFAULT_HOST
} in %(py5)sz0.0.0.0rI  z*build_server must refuse non-loopback hostloopback)z0%(py3)s in %(py8)s
{%(py8)s = %(py5)s(%(py6)s)
}strexc)r   r   r   r   z%(py10)sr   )z5%(py13)s in %(py18)s
{%(py18)s = %(py15)s(%(py16)s)
})r*  py15py16py18z%(py20)spy20r   zassert %(py23)spy23)rO  DEFAULT_PORTr   r   r   r   r   r   r   r   DEFAULT_HOSTrP  rG  r   r  rv  rw  )r   r   r   r   r   r  r   r   r   @py_assert12@py_assert17@py_assert14@py_format9r   @py_format19@py_format21@py_format22@py_format24s                     r%   <test_v6_port_8001_default_avoids_conflict_with_existing_8000r    s   ((0D0(D0000(D000000000000(000D0000000((0D0(D0000(D000000000000(000D0000000((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K%%9:<H IJJ  A@z@SX@zX%@@C@)@@@@@zX@@@z@@@@@@S@@@S@@@@@@@@@@@@X@@@@@@@@@@@@@@@@@@@@@@@@@C@@@C@@@@@@@@@@@@@@@@@@As   ?J) )
T3ITTc                `   | dz  }t        |       t        g i       }t        j                  ||      }t	        j
                  |d      }t        j                  D ]  }||v}|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndd	t        j                         v st        j                  |      rt        j                  |      nd	d
z  }dd|iz  }t        t        j                  |            d} y)uW   §3 보강 — 실제 HTTP 응답 본문에도 token prefix 가 노출되지 않는다.r   r   )r  r  Fr   r   r   r   r   r   zassert %(py4)sr   N)r?   r*   r   r1  r   r   r   r   r   r   r   r   r   r   r   )	r   r  r  r6  r   r   r   r   r   s	            r%   6test_v6_aggregator_response_does_not_leak_token_prefixr    s    //K{#b,F",,VTG::gE2D&55 "T!!!!vT!!!!!!v!!!v!!!!!!T!!!T!!!!!!!"r+   c            	        t         } 	 t        j                  ddt        |       dddgdddd	      }|j                  dk7  ry
|j                  xs dj                         D cg c]#  }|j                         s|j                         % }}dD ]  }|D ]	  }|j                  } ||      }| }|st        j                  d|       dz   dt        j                         v st        j                  |      rt        j                   |      ndt        j                   |      dt        j                         v st        j                  |      rt        j                   |      ndt        j                   |      dz  }	t#        t        j$                  |	            d
x}x}}  h d}
t'        |      |
z
  }| }|st        j                  dt)        |             dz   ddt        j                         v st        j                  |      rt        j                   |      ndiz  }t#        t        j$                  |            d
}y
# t        j                  t
        f$ r Y y
w xY wc c}w )uU   task-2526 expected_files 영역 (scripts/safe_cron_dispatch.py 등) 미수정 보장.gitz-Cdiffz--name-onlyzorigin/main...HEADTF   )capture_outputr|   checkr   Nr   r   )zscripts/safe_cron_dispatch.pyzscripts/safe-cron-dispatch.pyz+task-2524 must NOT modify task-2526 scope: zP
>assert not %(py5)s
{%(py5)s = %(py2)s
{%(py2)s = %(py0)s.endswith
}(%(py3)s)
}r7  	forbiddenr}   >   'tools/observability/dashboard_server.py'tools/observability/dashboard_view.html+tools/observability/dashboard_aggregator.py0tests/regression/test_dashboard_readonly_2524.pyz0unexpected files changed beyond expected_files: z
>assert not %(py0)sr~   r   )_WORKTREE_ROOT
subprocessrunr  TimeoutExpiredFileNotFoundError
returncodestdout
splitlinesstripendswithr   r   r   r   r   r   r   r   r  sorted)	repo_rootproclinechangedr  r7  r   r   r   r   expectedr   @py_format2s                r%   0test_v7_task_2526_scope_untouched_by_this_branchr    s   I	~~D#i.&-AUV
 !)-):(F(F(HYDJJLtzz|YGY X 	 	E~~ ~i0 00 0   >eWE v     I   I &  v   '0  I '0  I 1      	H L8#E9X9XXHPUXXXXXXXuXXXuXXXXXX+ %%'89  Zs   )I !I#7I#I I c            	     \   t         t        t        fD ]  } | j                  d      }dD ]  }| t        k(  r||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)un   git 미사용 환경에서도 동작하는 fallback — 본 task 가 task-2526 파일을 import 하지 않음.r9   r:   )safe_cron_dispatchzsafe-cron-dispatchr   r   r  r|   r   ztask-2526 scope referenced in r   r   N)r   r   
_TEST_PATHr   r   r   r   r   r   r   r   rR   r   r   )r   r|   r  r   r   r   s         r%   7test_v7_task_2526_scope_files_untouched_in_working_treer    s    %|Z@ [!!7!3E 	[I :%D(ZZZ9DZZZZZZ9ZZZ9ZZZZZZDZZZDZZZZ,J8==/*ZZZZZZZ	[[r+   c                    t         dz  dz  dz  t         dz  dz  dz  t         dz  dz  dz  t         dz  dz  dz  g} | D ]  }|j                  } |       }|st        j                  d	|j	                  t                      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         dz  dz  }t        d |j                         D              }g 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 )Nr	   r
   r   r   r   tests
regressionztest_dashboard_readonly_2524.pyzexpected file missing: zD
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.is_file
}()
}r   r   c              3  V   K   | ]!  }|j                         s|j                   # y wr   )is_filerR   )r   r   s     r%   r   z6test_v8_expected_files_exactly_four.<locals>.<genexpr>  s     La		166Ls   )))r   r   r   r   r   actual_in_obsr   z)unexpected files in tools/observability: r   r   )r  r  r   r   relative_tor   r   r   r   r   r   r  iterdirr   )
r  r   r   r   r   obs_dirr  r   r   r   s
             r%   #test_v8_expected_files_exactly_fourr    s    ?25NN ?25JJ ?25JJ </2SS	H  VyyUy{U{UU5ammN6S5TUUUUUUUqUUUqUUUyUUU{UUUUUUVw&8GL7??+<LLM C=   C 2C1B	C=  C C =CF	C C +C*B	  C C :C	  C C :C	 C C 2C1B	2=/B	C C C 0C/B	C Cr+   )r   zlist[dict[str, Any]] | Noner   z&dict[int, list[dict[str, Any]]] | None)r>   r   r(   None)rM   r   r(   r  )rM   r   ru   r   r(   r  )r(   r  )r   r   r(   r  )r(   r   )rK  r   r(   ztuple[Any, threading.Thread])<__doc__
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   r   r   rA  r  sysrQ  rU  urllib.errorrV  urllib.requestr   r   http.clientr   pathlibr   typingr   __file__resolverf   r  r  r>   removeinsert_OBS_DIRr   rO  r   r   r   r  r*   r?   rP   rS   rj   ry   r   r   r   r>  rG  ra  r  r  r  r  r  r  r)   r+   r%   <module>r     s  * #    	   
     ' &  
 h'')0077>>~#(("HHOOC'( 3~& ' G#o5x= HHOOAs8}%   77 //--
(^##%
 8<BF?6.!0	7?2=>'l\ 46V9|% !0K " YF	[ Cr+   