
    i#                    &   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mZ ddlZ ee      j#                         j$                  d   Z ej(                  d      d        Zd	 Zd
 Zd Zd Zd Zd Zd Zd Zd Zd Zd Z d Z!d Z"d Z#y)uM  tests/regression/test_chairman_audit.py — task-2471 회귀 테스트.

헤임달이 task-2471 1차 hardening 의 일환으로 신설한
``utils/audit_chairman_recovery.py`` 의 schema + atomic append 동작 검증.

- ``AUDIT_JSONL_PATH`` 경로 상수가
  ``memory/orchestration-audit/chairman-manual-recovery.jsonl`` 인지 확인.
- ``RECOVERY_RECORD_KEYS`` schema 6 키 모두 정의.
- ``append_recovery`` atomic append 동작 (디렉토리 자동 생성, JSONL 직렬화).
- ``read_recoveries`` 파일 부재 시 graceful (빈 리스트).

헤임달(개발2팀 테스터) 작성.
    )annotationsN)Path   module)scopec                    t         dz  dz  } | j                         st        j                  d       t        j
                  j                  dt        |             }g }d}||u}|}|r|j                  }d}||u}|}|slt        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  }
|j                  |
       |rt        j                  dfdf      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      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}}t        j
                  j'                  |      }|t(        j*                  |j,                  <   |j                  j/                  |       |S )u:   ``utils/audit_chairman_recovery.py`` 절대 경로 로드.utilszaudit_chairman_recovery.pyuM   utils/audit_chairman_recovery.py 미작성 — 헤임달 신규 모듈 대기"audit_chairman_recovery_test_aliasN)is not)z%(py2)s is not %(py5)sspec)py2py5z%(py7)spy7)z5%(py11)s
{%(py11)s = %(py9)s.loader
} is not %(py14)s)py9py11py14z%(py16)spy16r   zassert %(py19)spy19)	WORKSPACEexistspytestskip	importlibutilspec_from_file_locationstrloader
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprappend_format_boolopAssertionError_format_explanationmodule_from_specsysmodulesnameexec_module)	file_pathr   @py_assert1@py_assert4@py_assert3@py_assert0@py_assert10@py_assert13@py_assert12@py_format6@py_format8@py_format15@py_format17@py_format18@py_format20mods                   ;/home/jay/workspace/tests/regression/test_chairman_audit.pyacrr=      sf    G#&BBI[	
 >>11,c)nD 8t74t77474 777774t77777747774777t77777774777777777777777477777777777777
..
)
)$
/C CKK		KKC J    c                   d}t        | |      }|sd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        t        j                  |            dx}}| j                  }t        |t              }|s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	d
t        j                         v st        j                  t              rt        j
                  t              nd
t        j
                  |      dz  }t        t        j                  |            d}|j                  }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   }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}||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)u:   AUDIT_JSONL_PATH 가 정확한 상대 경로인지 검증.AUDIT_JSONL_PATH5assert %(py5)s
{%(py5)s = %(py0)s(%(py1)s, %(py3)s)
}hasattrr=   py0py1py3r   Nz5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstancepr   )rD   rE   r   py4zorchestration-auditinz%(py1)s in %(py3)spartsrE   rF   assert %(py5)sr   zchairman-manual-recovery.jsonl==z%(py1)s == %(py4)srE   rI   assert %(py6)spy6memory)rB   r    r!   r   r"   r#   r&   r'   r@   rG   r   rM   r   )r=   @py_assert2r/   r5   rH   r0   @py_format5rM   r1   @py_format4@py_format7s              r<   test_audit_jsonl_path_constantr\   1   s    *+73*++++++++7+++7++++++3+++3+++*++++++++++Aa::aaGGE ) E)))) E))) ))))))E)))E)))))))9888988888988889888888888888u8u8uur>   c                   d}t        | |      }|sd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        t        j                  |            dx}}t        | j                        }h d}||z
  }| }|s~t        j                  d|       d	z   d
dt        j                         v st        j                  |      rt        j
                  |      ndiz  }t        t        j                  |            d}y)u   6 schema 키 모두 정의.RECOVERY_RECORD_KEYSrA   rB   r=   rC   N>   tsreasontask_idto_state
from_stateevidence_pathsu   누락된 schema 키: z
>assert not %(py0)srD   missing)rB   r    r!   r   r"   r#   r&   r'   setr^   _format_assertmsg)	r=   rX   r/   r5   keysexpectedre   r.   @py_format2s	            r<    test_recovery_record_keys_schemark   =   s    ./73.////////7///7//////3///3///.//////////s''(DVHoG;:;::0	:::::::w:::w::::::r>   c                \   | j                  dddddg|      }|j                  } |       }|sddt        j                         v st	        j
                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      d	z  }t        t	        j                  |            d
x}}|j                  } |       }|sddt        j                         v st	        j
                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      d	z  }t        t	        j                  |            d
x}}|j                  }|j                  } |       }|sddt        j                         v st	        j
                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      t	        j                  |      dz  }t        t	        j                  |            d
x}x}}y
)u-   디렉토리/파일 부재 시 자동 생성.	task-2471RECOVERABLE_BLOCKEDMERGINGu   branch protection 해제 확인z'.tasks/evidence/task-2471/recovery.jsonra   rc   rb   r`   rd   	workspacezAassert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}out_path)rD   r   rI   NzBassert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.is_file
}()
}z]assert %(py6)s
{%(py6)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.parent
}.is_dir
}()
}rD   r   rI   rV   )append_recoveryr   r    r!   r   r"   r#   r&   r'   is_fileparentis_dir)r=   tmp_pathrr   r.   r0   rY   @py_assert5r[   s           r<   /test_append_recovery_creates_directory_and_filerz   K   sX   ""(0AB # H ???88?88??#?!!#!########8###8###?###!##########r>   c           	     	   | j                  ddddddg|       || j                  z  }|j                  d	      j                         j	                         }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}}t        j                  |d         }	h d}
|	j                   } |       }t#        |      }|
|k  }|sKt        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dt        j                         v st        j                  |	      rt        j                  |	      ndt        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}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}||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}||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dg}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }d d!|iz  }t        t        j                  |            dx}x}}y)%u9   append 된 JSONL 라인이 6 schema 키를 모두 포함.rm   rn   ro   okza.jsonzb.jsonrp   utf-8encoding   rQ   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slencontentrD   rE   rF   rV   assert %(py8)spy8Nr   >   r_   r`   ra   rb   rc   rd   )<=)za%(py0)s <= %(py9)s
{%(py9)s = %(py2)s(%(py7)s
{%(py7)s = %(py5)s
{%(py5)s = %(py3)s.keys
}()
})
}expected_keysrf   record)rD   r   rF   r   r   r   zassert %(py11)sr   ra   rS   rT   rU   rV   rc   rb   rd   )rt   r@   	read_textstrip
splitlinesr   r   r   r    r!   r"   r#   r&   r'   jsonloadsrh   rf   )r=   rx   targetr   rX   ry   r/   r[   @py_format9r   r   @py_assert6@py_assert8r.   @py_format10@py_format12r1   r0   rY   s                      r<   "test_append_recovery_record_schemar   [   s$   ( (+   ,,,F0668CCEGw<1<1<133ww<1ZZ
#FM !'..C..=.....=.......=...=......C...C..........................)+++++++++++++++++++,8#88#88888#8888888#88888888********************"#;(';;#';;;;;#';;;;#;;;';;;;;;;;r>   c           	        t        d      D ]  }| j                  d| ddd| g |       ! || j                  z  }|j                  d      j	                         j                         }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 
cg c]  }
t        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  }dd|iz  }t        t        j                  |            dx}}yc c}
w )u>   동일 파일에 2회 append 시 라인 2개 (덮어쓰기 X).   ztask-rn   ro   zreason-rp   r}   r~   rQ   r   r   linesr   r   r   Nra   )ztask-0task-1task-2z%(py0)s == %(py3)stask_idsrD   rF   rO   r   )rangert   r@   r   r   r   r   r   r   r    r!   r"   r#   r&   r'   r   r   )r=   rx   ir   r   rX   ry   r/   r[   r   lr   r.   rZ   r5   s                  r<   +test_append_recovery_appends_not_overwritesr   t   s   1X 
A3K,QC= 	 	

 ,,,Fg.446AACEu::?:33uu:278Q

1i(8H855855555855555558555855555555555 9s   ?I	c           	     ,   d}| j                  ddddg ||       || j                  z  }t        j                  |j	                         j                         j                         d         }|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}}y)u+   ``ts`` 인자가 그대로 기록되는지.z2026-05-07T01:23:45Zztask-XABt)ra   rc   rb   r`   rd   r_   rq   r   r_   rQ   )z%(py1)s == %(py3)s	custom_tsrN   rO   r   N)rt   r@   r   r   r   r   r   r   r   r#   r    r!   r"   r&   r'   )	r=   rx   r   r   r   r1   rX   rZ   r5   s	            r<    test_append_recovery_explicit_tsr      s    &I   ,,,FZZ((*002==?BCF$<$<9$$$$<9$$$<$$$$$$9$$$9$$$$$$$r>   c                   | j                  ddddg |       || j                  z  }t        j                  |j	                         j                         j                         d         }|d   }|j                  }d} ||      }|sd	dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      d
z  }t        t        j                  |            dx}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)u)   ``ts`` 미지정 시 ISO 8601 UTC 형식.ztask-Yr   r   r   rp   r   r_   ZzJassert %(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.endswith
}(%(py4)s)
}rs   NTrJ   rL   rN   rO   r   )rt   r@   r   r   r   r   r   endswithr    r!   r   r"   r#   r&   r'   r   )r=   rx   r   r   r_   r.   r0   ry   r[   r1   rX   rZ   r5   s                r<   'test_append_recovery_default_ts_iso8601r      s4      ,,,FZZ((*002==?BCF	B;;s;s22;s3"93"3""r>   c           	         t        j                  t              5  | j                  ddddg |       d d d        y # 1 sw Y   y xY w)N r   r   xrp   r   raises
ValueErrorrt   r=   rx   s     r<   *test_append_recovery_rejects_empty_task_idr      sH    	z	" 
 	 	

 
 
	   ;Ac           	         t        j                  t              5  | j                  ddddg |       d d d        y # 1 sw Y   y xY w)Nr   r   r   r   rp   r   r   s     r<   -test_append_recovery_rejects_empty_from_stater      sH    	z	" 
 	 	

 
 
r   c           	         t        j                  t              5  | j                  ddddg |       d d d        y # 1 sw Y   y xY w)Nr   r   r   r   rp   r   r   s     r<   +test_append_recovery_rejects_empty_to_stater      sH    	z	" 
 	 	

 
 
r   c                t   | j                  |      }g }||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)	u2   파일 부재 시 빈 리스트 반환 (graceful).rq   rQ   r   outr   rO   r   N)	read_recoveriesr   r   r    r!   r"   r#   r&   r'   )r=   rx   r   rX   r.   rZ   r5   s          r<   4test_read_recoveries_missing_file_returns_empty_listr      sm    




1C3"93"33"r>   c                   | j                  dddddg|       | j                  ddddd	g|       | j                  |
      }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   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   }d}	||	k(  }|slt        j                  d|fd||	f      t        j                  |      t        j                  |	      dz  }
dd|
iz  }t        t        j                  |            dx}x}}	y)u0   append 한 다음 read 로 동일 record 회수.ztask-AXYr1e1rp   ztask-Br2e2r   r   rQ   r   r   r   r   r   r   Nr   ra   rS   rT   rU   rV   r   )rt   r   r   r   r   r    r!   r"   r#   r&   r'   )r=   rx   r   rX   ry   r/   r[   r   r1   r0   rY   s              r<   test_read_recoveries_round_tripr      s   v   v   



1Cs8q8q=8q33ss8qq6)(((((((((((((((((((q6)(((((((((((((((((((r>   c                   || j                   z  }|j                  j                  dd       |j                  dd       | j	                  |      }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 	cg c]  }	|	d   	 }
}	ddg}|
|k(  }|slt        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}}yc c}	w )u!   JSON parse 실패 라인은 skip.T)parentsexist_okz{"task_id": "task-1", "ts": "T", "from_state": "A", "to_state": "B", "reason": "x", "evidence_paths": []}
garbage line not json
{"task_id": "task-2", "ts": "T", "from_state": "A", "to_state": "B", "reason": "y", "evidence_paths": []}
r}   r~   r   r   rQ   r   r   r   r   r   r   Nra   r   r   rS   rT   rU   rV   )r@   rv   mkdir
write_textr   r   r   r   r    r!   r"   r#   r&   r'   )r=   rx   r   r   rX   ry   r/   r[   r   rr1   r0   rY   s                r<   *test_read_recoveries_skips_malformed_linesr      sQ   ,,,F
MMt4
	B
    



1Cs8q8q=8q33ss8q"%&QAiL&>&8X*>>&*>>>>>&*>>>>&>>>*>>>>>>>>&s   &G2c                   | j                  ddddg |       || j                  z  }t        j                  |j	                         j                         j                         d         }|d   }g }||k(  }|slt        j                  d|fd	||f      t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}}y)uD   evidence_paths=None 또는 빈 sequence 시 빈 리스트 직렬화.ztask-Zr   r   r   rp   r   rd   rQ   rS   rT   rU   rV   N)rt   r@   r   r   r   r   r   r   r   r#   r&   r'   )	r=   rx   r   r   r1   r0   rX   rY   r[   s	            r<   +test_evidence_paths_none_becomes_empty_listr     s       ,,,FZZ((*002==?BCF"#)r)#r))))#r)))#)))r)))))))r>   )$__doc__
__future__r   builtinsr    _pytest.assertion.rewrite	assertionrewriter   importlib.utilr   r   r)   pathlibr   r   __file__resolver   r   fixturer=   r\   rk   rz   r   r   r   r   r   r   r   r   r   r   r    r>   r<   <module>r      s    #      
  N""$,,Q/	 h  ,	;$ <26&%".	
	
	
")2?.*r>   