
    )Ti#                     `   d 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mZ ddlZe	j                  j!                  d e ee      j&                  j&                  j&                               ddlmZmZ dedee   fdZ G d d	      Z G d
 d      Z G d d      Z G d d      Zy)u)   utils/audit_logger.py 테스트 스위트    N)Path)log_batch_operationslog_file_operationpathreturnc                     g }| j                         r\| j                  d      j                         D ]9  }|j                         }|s|j	                  t        j                  |             ; |S )u5   JSONL 파일을 파싱하여 레코드 목록 반환.zutf-8)encoding)exists	read_text
splitlinesstripappendjsonloads)r   recordslines      4/home/jay/workspace/utils/tests/test_audit_logger.py_read_jsonlr      s]    G{{}NNGN4??A 	1D::<Dtzz$/0	1 N    c                   :    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
y	)
TestLogFileOperationBasicu,   log_file_operation() 기본 동작 테스트c                 t   |dz  }t        ddd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
t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}}y)u&   레코드가 audit 파일에 기록됨audit-trail.jsonlztask-001/tmp/foo.pyWrite
audit_path   ==z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenr   py0py1py3py6assert %(py8)spy8N)r   strr   r"   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanation	selftmp_pathtrailr   @py_assert2@py_assert5@py_assert4@py_format7@py_format9s	            r   test_record_written_to_filez5TestLogFileOperationBasic.test_record_written_to_file)   s    ..:}g#e*Ue$7| q |q    |q      s   s      7   7   |   q       r   c                    |dz  }t        dddt        |             t        |      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}||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}||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.   레코드에 필수 필드가 모두 포함됨r   ztask-002z/tmp/bar.pyEditr   r   task_idr   z%(py1)s == %(py4)sr%   py4assert %(py6)sr'   Nfiletool	operationwriteagentsubagentts)in)z%(py1)s in %(py3)srec)r%   r&   assert %(py5)spy5)r   r*   r   r+   r,   r0   r1   r2   r-   r.   r/   )r4   r5   r6   rL   @py_assert0@py_assert3r7   @py_format5r:   @py_format4@py_format6s              r   test_record_fields_presentz4TestLogFileOperationBasic.test_record_fields_present0   s   ..:}fUT% #9~++~++++~+++~++++++++++6{+m+{m++++{m+++{+++m+++++++6{$f${f$$$${f$$${$$$f$$$$$$$;*7*7****7******7*******7|)z)|z))))|z)))|)))z)))))))ts{tstssr   c                    ddl m } |dz  }t        dddt        |             t        |      d   }|j	                  |d   j                  d	d
            }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}}y)u   ts 필드가 ISO 8601 형식임r   )datetimer   ztask-003	/tmp/x.pyr   r   rJ   Zz+00:00N)is not)z%(py0)s is not %(py3)sdt)r$   r&   rM   rN   )rV   r   r*   r   fromisoformatreplacer+   r,   r-   r.   r/   r0   r1   r2   )
r4   r5   rV   r6   rL   rZ   r7   @py_assert1rR   rS   s
             r   test_ts_is_iso8601z,TestLogFileOperationBasic.test_ts_is_iso8601<   s    %..:{GE
S% ###CI$5$5c8$DEr~rrrr   c           	         |dz  }t        d      D ]"  }t        d|dd| dd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t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y)u.   여러 번 호출하면 레코드가 누적됨r      task-03d/tmp/f.pyr   r   r   r!   r"   r   r#   r(   r)   N)ranger   r*   r   r"   r+   r,   r-   r.   r/   r0   r1   r2   )
r4   r5   r6   ir   r7   r8   r9   r:   r;   s
             r   test_multiple_calls_appendz4TestLogFileOperationBasic.test_multiple_calls_appendG   s    ..q 	aAqg&3UXY^U_`	ae$7| q |q    |q      s   s      7   7   |   q       r   c           	      \   |dz  }t        ddddt        |             t        |      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   operation 파라미터가 레코드에 반영됨r   ztask-004z	/tmp/z.pyr   delete)rF   r   r   rF   r   r@   rA   rC   r'   N)r   r*   r   r+   r,   r0   r1   r2   )	r4   r5   r6   rL   rO   rP   r7   rQ   r:   s	            r   test_custom_operation_parameterz9TestLogFileOperationBasic.test_custom_operation_parameterO   s    ..:{Gx\_`e\fg% #;+8+8++++8++++++8+++++++r   c                    |dz  dz  dz  }t        dddt        |             |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}}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t        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}	}y)u>   audit 파일 부모 디렉토리가 없어도 자동 생성됨nesteddeepr   ztask-005z/tmp/abc.pyr   r   zAassert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}r6   r$   py2rB   Nr   r   r!   r"   r   r#   r(   r)   )r   r*   r
   r-   r.   r+   r/   r0   r1   r2   r   r"   r,   )r4   r5   r6   r]   rP   rQ   r   r7   r8   r9   r:   r;   s               r   test_creates_parent_directoryz7TestLogFileOperationBasic.test_creates_parent_directoryV   s   8#f,/BB:}g#e*U|||~~uu|~e$7| q |q    |q      s   s      7   7   |   q       r   c           	      j   |dz  }t        d      D ]!  }t        d| d| ddt        |             # |j                         j	                         D cg c]  }|j                         s| }}|D ]8  }t        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}; yc c}w )u   각 줄이 유효한 JSON임r      ra   rc   rd   r   r   z5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstanceparseddict)r$   r%   ro   rB   N)re   r   r*   r   r   r   r   r   rs   ru   r-   r.   r+   r/   r0   r1   r2   )
r4   r5   r6   rf   l	raw_linesr   rt   rP   rQ   s
             r   test_each_line_is_valid_jsonz6TestLogFileOperationBasic.test_each_line_is_valid_json_   s   ..q 	]Aqc{fQCsOWQTUZQ[\	] % 1 < < >L1!'')QL	L 	,DZZ%Ffd++++++++:+++:++++++f+++f++++++d+++d++++++++++	, Ms   F0+F0N)__name__
__module____qualname____doc__r<   rT   r^   rg   rj   rp   rx    r   r   r   r   &   s(    6!
	!,!,r   r   c                   (    e Zd ZdZd Zd Zd Zd Zy)TestLogFileOperationInvalidArgsu!   잘못된 인자 처리 테스트c           	          |dz  }t        j                  t        t        f      5  t	        dddt        |             ddd       y# 1 sw Y   yxY w)u    빈 task_id는 ValueError 발생r    r   r   r   Npytestraises
ValueError	TypeErrorr   r*   r4   r5   r6   s      r   test_empty_task_id_raisesz9TestLogFileOperationInvalidArgs.test_empty_task_id_raisesr   sI    ..]]J	23 	Rr='c%jQ	R 	R 	R   AAc           	          |dz  }t        j                  t        t        f      5  t	        dddt        |             ddd       y# 1 sw Y   yxY w)u   None task_id는 예외 발생r   Nr   r   r   r   r   s      r   test_none_task_id_raisesz8TestLogFileOperationInvalidArgs.test_none_task_id_raisesx   sI    ..]]J	23 	Tt]GE
S	T 	T 	Tr   c           	          |dz  }t        j                  t        t        f      5  t	        dddt        |             ddd       y# 1 sw Y   yxY w)u!   빈 filepath는 ValueError 발생r   task-xr   r   r   Nr   r   s      r   test_empty_filepath_raisesz:TestLogFileOperationInvalidArgs.test_empty_filepath_raises~   sI    ..]]J	23 	MxWUL	M 	M 	Mr   c           	          |dz  }t        j                  t        t        f      5  t	        dddt        |             ddd       y# 1 sw Y   yxY w)u   빈 tool은 ValueError 발생r   r   r   r   r   Nr   r   s      r   test_empty_tool_raisesz6TestLogFileOperationInvalidArgs.test_empty_tool_raises   sI    ..]]J	23 	Sxs5zR	S 	S 	Sr   N)ry   rz   r{   r|   r   r   r   r   r}   r   r   r   r   o   s    +RTMSr   r   c                   (    e Zd ZdZd Zd Zd Zd Zy)TestLogBatchOperationsu    log_batch_operations() 테스트c                    |dz  }t        d      D cg c]  }d| d
 }}t        d|d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t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}yc c}w )u5   배치 함수가 모든 파일 레코드를 기록함r      z	/tmp/filerd   ztask-010r   r   r   r!   r"   r   r#   r(   r)   N)re   r   r*   r   r"   r+   r,   r-   r.   r/   r0   r1   r2   )r4   r5   r6   rf   	filepathsr   r7   r8   r9   r:   r;   s              r   test_batch_writes_all_filesz2TestLogBatchOperations.test_batch_writes_all_files   s    ..16q:Ay3':	:ZGE
Se$7| q |q    |q      s   s      7   7   |   q        ;s   Ec                 t   |dz  }t        dg d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	t	        j                  |      t	        j                  |      d
z  }dd|iz  }t        t	        j                  |            dx}x}}y)u5   빈 파일 목록은 레코드를 기록하지 않음r   ztask-011r   r   r   r   r!   r"   r   r#   r(   r)   N)r   r*   r   r"   r+   r,   r-   r.   r/   r0   r1   r2   r3   s	            r   $test_batch_empty_list_writes_nothingz;TestLogBatchOperations.test_batch_empty_list_writes_nothing   s    ..ZWULe$7| q |q    |q      s   s      7   7   |   q       r   c                    |dz  }g d}t        d|dt        |             t        |      }d |D        }t        |      }|sddt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      d	z  }t        t        j                  |            d
x}}y
)u,   배치 레코드 모두 task_id가 동일함r   )z	/tmp/a.pyz	/tmp/b.pyz	/tmp/c.pytask-012r>   r   c              3   ,   K   | ]  }|d    dk(    yw)r?   r   Nr}   ).0rs     r   	<genexpr>zQTestLogBatchOperations.test_batch_records_have_correct_task_id.<locals>.<genexpr>   s     ?!1Y<:-?s   z,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}allrn   N)r   r*   r   r   r-   r.   r+   r/   r0   r1   r2   )r4   r5   r6   r   r   r]   rP   rQ   s           r   'test_batch_records_have_correct_task_idz>TestLogBatchOperations.test_batch_records_have_correct_task_id   s    ..;	ZFs5zRe$?w??s?????????s???s??????????????r   c                    |dz  }ddg}t        d|dt        |             t        |      D ch c]  }|d   	 }}t        |      }||k(  }|s#t	        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                  |      dz  }dd|iz  }	t        t	        j                  |	            dx}}yc c}w )u=   배치 레코드의 file 필드가 입력 경로와 일치함r   rW   z	/tmp/y.pyztask-013r   r   rD   r   )z0%(py0)s == %(py5)s
{%(py5)s = %(py2)s(%(py3)s)
}recorded_filessetr   )r$   ro   r&   rN   assert %(py7)spy7N)r   r*   r   r   r+   r,   r-   r.   r/   r0   r1   r2   )
r4   r5   r6   r   r   r   r9   r]   rS   @py_format8s
             r   $test_batch_records_correct_filepathsz;TestLogBatchOperations.test_batch_records_correct_filepaths   s    .. +.	ZGE
S-8-?@!F)@@!$Y/~////~//////~///~///////////////Y///Y////////// As   E8N)ry   rz   r{   r|   r   r   r   r   r}   r   r   r   r      s    *!!@0r   r   c                       e Zd ZdZd Zd Zy)TestConcurrentWritesu9   동시 쓰기 시 파일 락으로 안전하게 기록됨c                    |dz  d}g dt         ffd}t        |      D cg c]  }t        j                  ||f       }}|D ]  }|j	                           |D ]  }|j                            }|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
}t              }	t        |	      }
|
|k(  }|s#t        j                   d|fd|
|f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |	      rt        j                  |	      ndt        j                  |
      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 )uC   N개 스레드가 동시에 기록해도 모든 레코드가 남음r      idxc                     	 t        d| dd|  ddt                     y # t        $ r}j                  |       Y d }~y d }~ww xY w)Nra   rb   z/tmp/concurrent_rd   r   r   )r   r*   	Exceptionr   )r   eerrorsr6   s     r   workerzHTestConcurrentWrites.test_concurrent_writes_no_data_loss.<locals>.worker   sP    !"U3s)#48HS6QSZgjkpgqr !a  !s    $ 	AAAtargetargsu   스레드 오류 발생: z
>assert not %(py0)sr$   r   Nr   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py5)sr"   r   	n_threadsr$   r%   r&   rN   r   r   )intre   	threadingThreadstartjoinr+   _format_assertmsgr-   r.   r/   r0   r1   r2   r   r"   r,   )r4   r5   r   r   rf   threadstr]   @py_format2r   r7   r9   rS   r   r   r6   s                 @@r   #test_concurrent_writes_no_data_lossz8TestConcurrentWrites.test_concurrent_writes_no_data_loss   sw   ..	"$	! 	! HMYGWX!9##6=XX 	AGGI	 	AFFH	 z?z??6vh???????6???6??????e$7|(|y((((|y((((((s(((s((((((7(((7(((|((((((y(((y((((((( Ys   I c                    |dz  d}dt         ffd}t        |      D cg c]  }t        j                  ||f       }}|D ]  }|j	                           |D ]  }|j                           j                         j                         D cg c]  }|j                         s| }}t        |      }	|	|k(  }
|
s#t        j                  d|
fd|	|f      dt        j                         v st        j                  t              rt        j                  t              ndd	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |	      d
t        j                         v st        j                  |      rt        j                  |      nd
dz  }dd|iz  }t!        t        j"                  |            dx}	}
|D ]  }t%        j&                  |        yc c}w c c}w )u7   동시 쓰기 후에도 모든 줄이 유효한 JSON임r      r   c                 D    t        d|  d|  ddt                     y )Nra   rc   rd   r   r   )r   r*   )r   r6   s    r   r   zJTestConcurrentWrites.test_concurrent_writes_all_valid_json.<locals>.worker   s'    se}se3.?UXY^U_`r   r   r   r   r"   rw   r   r   r   r   N)r   re   r   r   r   r   r   r   r   r"   r+   r,   r-   r.   r/   r0   r1   r2   r   r   )r4   r5   r   r   rf   r   r   rv   rw   r7   r9   rS   r   r   r6   s                 @r   %test_concurrent_writes_all_valid_jsonz:TestConcurrentWrites.test_concurrent_writes_all_valid_json   sk   ..		a 	a HMYGWX!9##6=XX 	AGGI	 	AFFH	 !& 1 < < >L1!'')QL	L9~*~****~******s***s******9***9***~**************** 	DJJt	 Y Ms   HH	'H	N)ry   rz   r{   r|   r   r   r}   r   r   r   r      s    C),r   r   )r|   builtinsr-   _pytest.assertion.rewrite	assertionrewriter+   r   ossysr   timepathlibr   r   r   insertr*   __file__parentutils.audit_loggerr   r   listru   r   r   r   r   r   r}   r   r   <module>r      s    /    	 
     3tH~,,33::; < Gd tDz  A, A,RS SB 0  0P* *r   