
    3ii                     N   d Z ddlZddlmc mZ ddlZddlZddl	m
Z
 ddlZdZeej                  vrej                  j                  de       ddlmZmZ  G d d      Z G d d	      Z G d
 d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Zy)u  TDD RED Phase 4 테스트 모음.

대상 함수 (아직 미구현):
  - orchestrator.event_bus.scan_done_events : memory/events/*.done 스캔 → incoming/ 복사

기능 요약:
  - events_dir/*.done 파일을 스캔하여 incoming_dir/로 복사
  - 원본 .done 파일은 보존 (Layer 2 독립성)
  - 이미 incoming/ 또는 processed/에 있으면 건너뜀 (중복 방지)
  - symlink이면 거부 (보안)

작성자 : 헤임달 (dev2-team tester)
날짜   : 2026-03-24
    N)Pathz/home/jay/workspace)consume_eventscan_done_eventsc                   (    e Zd ZdZd Zd Zd Zd Zy)TestSingleTeamDoneuC   T1: 단일 .done 파일 스캔 → Layer 2·3 독립 동작 확인.c                    |dz  }|dz  }|dz  }|j                          |j                          |j                          d}||z  j                  d       t        t        |      t        |      t        |            }||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}||z  }
|
j                  } |       }|st	        j                  d      dz   dt        j                         v st	        j                  |      rt	        j                  |      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}
x}}y)ut   events/에 .done 파일이 있으면 scan_done_events가 incoming/에 복사하고 파일명을 반환해야 한다.eventsincoming	processedpipeline-001.donedoneinz%(py0)s in %(py2)s
event_filecopiedpy0py2u   복사된 파일 목록에 u   이 없음: 
>assert %(py4)spy4Nu0   incoming/에 .done 파일이 복사되지 않음O
>assert %(py6)s
{%(py6)s = %(py4)s
{%(py4)s = (%(py0)s / %(py1)s).exists
}()
}incoming_dirr   py1r   py6mkdir
write_textr   str
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgAssertionError_format_explanationexists)selftmp_path
events_dirr   processed_dirr   r   @py_assert1@py_format3@py_format5@py_assert2@py_assert3@py_assert5@py_format7s                 5/home/jay/workspace/orchestrator/tests/test_phase4.py&test_scan_copies_done_file_to_incomingz9TestSingleTeamDone.test_scan_copies_done_file_to_incoming*   s   (
*, ;.(
	j	 ,,V4!#j/3|3Dc-FXYV#ccczVcccccczccczccccccVcccVcccc'B:,l[aZb%cccccccz)g)11g13g3gg5ggggggggggggggggzgggzggg1ggg3gggggg    c                    |dz  }|dz  }|dz  }|j                          |j                          |j                          d}||z  j                  d       t        t        |      t        |      t        |             ||z  }|j                  } |       }|st        j                  d      dz   dt        j                         v st        j                  |      rt        j                  |      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}x}}y)ut   scan_done_events 호출 후 원본 .done 파일이 events/에 그대로 남아 있어야 한다 (Layer 2 독립성).r	   r
   r   r   r   uF   scan 후 원본 .done 파일이 삭제됨 — Layer 2 독립성 위반r   r-   r   r   Nr   r   r   r    r*   r!   r'   r#   r$   r%   r&   r(   r)   
r+   r,   r-   r   r.   r   r2   r3   r4   r5   s
             r6   &test_scan_preserves_original_done_filez9TestSingleTeamDone.test_scan_preserves_original_done_file;   s
   (
*, ;.(
	j	 ,,V4Z#l*;S=OPZ'{'//{/1{1{{3{{{{{{{
{{{
{{{{{{Z{{{Z{{{/{{{1{{{{{{r8   c                    |dz  }|dz  }|dz  }|j                          |j                          |j                          d}||z  j                  d       t        t        |      t        |      t        |             t	        t        |      t        |      |      }d}||u }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
z  }	t        j                  d      dz   d|	iz  }
t        t        j                  |
            dx}}||z  }|j                  } |       }|st        j                  d      dz   dt        j                         v st        j                  |      rt        j                  |      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}x}}||z  }|j                  } |       }| }|st        j                  d      dz   dt        j                         v st        j                  |      rt        j                  |      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}x}x}}y)uq   scan → incoming 복사 후 consume_event가 incoming → processed로 이동하고 True를 반환해야 한다.r	   r
   r   r   r   Tisz%(py0)s is %(py3)sresultr   py3u5   scan 후 consume_event가 True를 반환하지 않음
>assert %(py5)spy5Nu*   consume 후 processed/에 파일이 없음r   r.   r   r   u/   consume 후 incoming/에 파일이 남아있음S
>assert not %(py6)s
{%(py6)s = %(py4)s
{%(py4)s = (%(py0)s / %(py1)s).exists
}()
}r   r   r   r   r    r   r!   r"   r#   r$   r%   r&   r'   r(   r)   r*   )r+   r,   r-   r   r.   r   rA   r2   r/   @py_format4@py_format6r3   r4   r5   @py_assert7@py_format8s                   r6   0test_consume_event_after_scan_moves_to_processedzCTestSingleTeamDone.test_consume_event_after_scan_moves_to_processedK   s    (
*, ;.(
	j	 ,,V4Z#l*;S=OPs<0#m2DjQVv~VVVvVVVVVVvVVVvVVVVVVVVVVVVV
*b*22b24b4bb6bbbbbbbbbbbbbbbb
bbb
bbb2bbb4bbbbbb :-j-55j57j77j7jj9jjjjjjjLjjjLjjjjjj:jjj:jjj5jjj7jjjjjjr8   c                 D   |dz  }|dz  }|dz  }|j                          |j                          |j                          d}||z  j                  d       t        t        |      t        |      t        |             t	        t        |      t        |      |       ||z  }|j
                  } |       }|st        j                  d      dz   dt        j                         v st        j                  |      rt        j                  |      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}x}}y)uX   consume_event 완료 후에도 events/의 원본 .done 파일이 보존되어야 한다.r	   r
   r   r   r   uV   consume_event 완료 후 원본 .done 파일이 삭제됨 — Layer 2 독립성 위반r   r-   r   r   N)r   r   r   r    r   r*   r!   r'   r#   r$   r%   r&   r(   r)   r;   s
             r6   .test_original_done_file_survives_consume_eventzATestSingleTeamDone.test_original_done_file_survives_consume_event^   s   (
*, ;.(
	j	 ,,V4Z#l*;S=OPc,']);ZH #	m#
&	m   	m  	m[l[ll	m 	mflfl	m 	mTlTl 	m 	mclcl 	m 	mflfl	m 	mTlTl $	m 	mclcl $	m 	mclcl 	m 	mclcl 	m 	m 	mYlYl	m 	m 	mr8   N)__name__
__module____qualname____doc__r7   r<   rL   rN    r8   r6   r   r   '   s    Mh"| k&mr8   r   c                   "    e Zd ZdZd Zd Zd Zy)TestDuplicatePreventionu]   T2: 중복 방지 — 이미 incoming/ 또는 processed/에 있으면 재복사하지 않음.c                 V   |dz  }|dz  }|dz  }|j                          |j                          |j                          d}||z  j                  d       t        t        |      t        |      t        |            }t        t        |      t        |      t        |            }||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}||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}t        |      }d}||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                  |      t	        j                  |      dz  }t	        j                  dt        |       d      dz   d|iz  }t        t	        j                  |            dx}x}}y)u_   scan_done_events 2회 연속 호출 시 두 번째 호출에서는 복사 0건이어야 한다.r	   r
   r   zpipeline-002.doner   r   r   r   first_copiedr   u   첫 번째 scan에서 u   이 복사되지 않음r   r   Nnot inz%(py0)s not in %(py2)ssecond_copiedu1   두 번째 scan에서 이미 incoming/에 있는 u-   을 다시 복사함 — 중복 방지 실패r   ==z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenr   r   rC   r   u6   두 번째 scan 복사 건수가 0이어야 하는데    건임
>assert %(py8)spy8)r   r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   r_   )r+   r,   r-   r   r.   r   rW   r[   r/   r0   r1   r2   r4   @py_assert4r5   @py_format9s                   r6   7test_second_scan_returns_empty_when_already_in_incomingzOTestDuplicatePrevention.test_second_scan_returns_empty_when_already_in_incomingz   s   (
*, ;.(
	j	 ,,V4'J\9JCP]L^_(Z#l:KSQ^M_`\)gggz\ggggggzgggzgggggg\ggg\gggg-CJ<Of+gggggggm+	ygxgx	ym	y 	yrxrx	y 	y`x`x 	y 	yoxox 	y 	yrxrx	y 	y`x`x ,	y 	yoxox ,	y 	ygxgx>zlJwx	y 	y 	yexex	y 	y =!{Q{!Q&{{{!Q{{{{{{s{{{s{{{{{{={{{={{{!{{{Q{{{*`aderas`ttz({{{{{{{{r8   c                    |dz  }|dz  }|dz  }|j                          |j                          |j                          d}||z  j                  d       ||z  j                  d       t        t        |      t        |      t        |            }||v}|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndd	t        j                         v st	        j                  |      rt	        j                  |      nd	d
z  }t	        j                  d| d      dz   d|iz  }	t        t	        j                  |	            d}y)uX   processed/에 이미 있는 파일은 scan_done_events가 복사하지 않아야 한다.r	   r
   r   zpipeline-already-done.doner   rX   rZ   r   r   r   u   processed/에 이미 있는 u>   을 scan이 복사함 — 중복 파이프라인 시작 위험r   r   Nr   r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   )
r+   r,   r-   r   r.   r   r   r/   r0   r1   s
             r6   )test_scan_skips_file_already_in_processedzATestDuplicatePrevention.test_scan_skips_file_already_in_processed   so   (
*, ;.1
	j	 ,,V4		#//7!#j/3|3Dc-FXY f$	uctct	uf	u 	untnt	u 	u\t\t 	u 	uktkt 	u 	untnt	u 	u\t\t %	u 	uktkt %	u 	uctct)*5st	u 	u 	uatat	u 	ur8   c                    |dz  }|dz  }|dz  }|j                          |j                          |j                          d}d}d}||z  j                  d       ||z  j                  d       ||z  j                  d       ||z  j                  d       ||z  j                  d       t        t        |      t        |      t        |            }||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}	||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}	||v}	|	st	        j
                  d|	fd||f      dt        j                         v st	        j                  |      rt	        j                  |      nddt        j                         v st	        j                  |      rt	        j                  |      nddz  }
t	        j                  d| d      dz   d|
iz  }t        t	        j                  |            d}	y)ui   신규 .done만 복사하고, 이미 incoming/ 또는 processed/에 있는 것은 건너뛰어야 한다.r	   r
   r   zpipeline-new.donezpipeline-in-incoming.donezpipeline-in-processed.doner   r   r   new_filer   r   u   신규 파일    이 복사 목록에 없음r   r   NrX   rZ   in_incomingu   이미 incoming/에 있는 u   이 다시 복사됨in_processedu   이미 processed/에 있는 rh   )r+   r,   r-   r   r.   rk   rm   rn   r   r/   r0   r1   s               r6   *test_scan_copies_only_new_files_when_mixedzBTestDuplicatePrevention.test_scan_copies_only_new_files_when_mixed   sG   (
*, ;.&13	h	**62	k	!--f5	l	"..v6		#//7		%11&9!#j/3|3Dc-FXY6!YYYx6YYYYYYxYYYxYYYYYY6YYY6YYYY^H:=X#YYYYYYY&(iii{&iiiiii{iii{iiiiii&iii&iiii,G}Th*iiiiiii6)lll|6llllll|lll|llllll6lll6llll-I,Wk+lllllllr8   N)rO   rP   rQ   rR   rf   ri   ro   rS   r8   r6   rU   rU   w   s    g|*u(mr8   rU   c                   "    e Zd ZdZd Zd Zd Zy)TestConcurrentDoneFilesuO   T3: 여러 .done 파일 동시 생성 → 모두 독립적으로 처리 가능.c           	         |dz  }|dz  }|dz  }|j                          |j                          |j                          g d}|D ]  }||z  j                  d        t        t        |      t        |      t        |            }t	        |      }t	        |      }	||	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                  |      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  }t        j                  dt	        |       dt	        |       d      dz   d|i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z   d|iz  }t        t        j                  |            d}||z  }|j                  } |       }|st        j                  d| d      dz   dt        j                         v st        j                  |      rt        j                  |      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}x}} y)ui   events/에 여러 .done 파일이 있을 때 scan_done_events가 모두 incoming/에 복사해야 한다.r	   r
   r   )zteam-alpha.donezteam-beta.donezteam-gamma.donezteam-delta.donezteam-epsilon.doner   r\   )zN%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py8)s
{%(py8)s = %(py5)s(%(py6)s)
}r_   r   event_files)r   r   rC   rE   r   rc   u   복사 건수가 u   이어야 하는데 ra   
>assert %(py10)spy10Nr   r   efr   rl   r   r   u   incoming/에 
   이 없음r   r   r   )r   r   r   r    r_   r!   r"   r#   r$   r%   r&   r'   r(   r)   r*   )r+   r,   r-   r   r.   rs   rv   r   r2   rJ   rd   re   @py_format11r/   r0   r1   r3   r4   r5   s                      r6   'test_scan_copies_all_done_files_at_oncez?TestConcurrentDoneFiles.test_scan_copies_all_done_files_at_once   s   (
*, ;.
  	1B"_((0	1 "#j/3|3Dc-FXY6{}c+.}{..}}}{.}}}}}}s}}}s}}}}}}6}}}6}}}{}}}}}}c}}}c}}}}}}+}}}+}}}.}}}2CCDTCUUijmntjuivv|0}}}}}}}} 	PB<CCC2CCCCCC2CCC2CCCCCCCCCCCCCB4'B!CCCCCCC 2%O%--O-/O/OO=J1OOOOOOOLOOOLOOOOOO2OOO2OOO-OOO/OOOOOO	Pr8   c                    |dz  }|dz  }|dz  }|j                          |j                          |j                          g d}|D ]  }||z  j                  d        t        t        |      t        |      t        |             |D ]  }t	        t        |      t        |      |      }d}||u }	|	st        j                  d|	fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
z  }
t        j                  | d      dz   d|
iz  }t        t        j                  |            dx}	}||z  }|j                  } |       }|st        j                  d| d      dz   dt        j                         v st        j                  |      rt        j                  |      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}x}} y)u\   incoming/에 복사된 각 파일을 독립적으로 consume_event 할 수 있어야 한다.r	   r
   r   )zteam-A.donezteam-B.donezteam-C.doner   Tr>   r@   rA   rB   u'    소비 시 True를 반환하지 않음rD   rE   Nu   처리 후 processed/에 rw   r   r.   rv   r   rG   )r+   r,   r-   r   r.   rs   rv   rA   r2   r/   rH   rI   r3   r4   r5   s                  r6   3test_each_copied_file_can_be_consumed_independentlyzKTestConcurrentDoneFiles.test_each_copied_file_can_be_consumed_independently   s   (
*, ;.C 	1B"_((0	1 	Z#l*;S=OP 	]B"3|#4c-6H"MF!Q6T>QQQ6TQQQQQQ6QQQ6QQQTQQQbT)P#QQQQQQQ!B&\&..\.0\0\\4MbTQ[2\\\\\\\M\\\M\\\\\\B\\\B\\\.\\\0\\\\\\	]r8   c                 2   |dz  }|dz  }|dz  }|j                          |j                          |j                          g d}|D ]  }||z  j                  d        t        t        |      t        |      t        |             |D ]  }||z  }|j                  } |       }	|	st        j                  d| d      dz   d	t        j                         v st        j                  |      rt        j                  |      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}x}}	 y)u_   여러 .done 동시 스캔 후에도 모든 원본 파일이 events/에 보존되어야 한다.r	   r
   r   )zteam-X.donezteam-Y.donezteam-Z.doner   u   원본 u3   이 scan 후 삭제됨 — Layer 2 독립성 위반r   r-   rv   r   Nr:   )r+   r,   r-   r   r.   rs   rv   r2   r3   r4   r5   s              r6   +test_scan_preserves_all_original_done_fileszCTestConcurrentDoneFiles.test_scan_preserves_all_original_done_files   s2   (
*, ;.C 	1B"_((0	1 	Z#l*;S=OP 	qBOpO++p+-p-pp<o/pppppppJpppJpppppppppppp+ppp-pppppp	qr8   N)rO   rP   rQ   rR   ry   r{   r}   rS   r8   r6   rq   rq      s    YP4](qr8   rq   c                   "    e Zd ZdZd Zd Zd Zy)!TestManualAndAutoChainCoexistenceua   T4: .done 원본 보존 + .done.notified 생성 — 양쪽 경로가 충돌 없이 독립 동작.c                    |dz  }|dz  }|dz  }|j                          |j                          |j                          d}d}||z  j                  d       ||z  j                  d       t        t        |      t        |      t        |            }||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z   d|	iz  }
t        t	        j                  |
            d}||z  }|j                  } |       }|st	        j                  d      dz   dt        j                         v st	        j                  |      rt	        j                  |      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}x}}||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z   d|	iz  }
t        t	        j                  |
            d}||z  }|j                  } |       }| }|st	        j                  d      dz   dt        j                         v st	        j                  |      rt	        j                  |      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}x}x}}y)us   scan_done_events가 incoming/에 복사하는 동안 .done.notified 파일이 공존해도 충돌 없어야 한다.r	   r
   r   zpipeline-chain.donezpipeline-chain.done.notifiedr   notifiedr   r   r   r   r   u+   scan이 .done 파일을 복사하지 않음r   r   Nu#   incoming/에 .done 파일이 없음r   r   r   rX   rZ   notified_markeru*   .done.notified가 scan 결과에 포함됨u(   .done.notified가 incoming/에 복사됨rF   r   )r+   r,   r-   r   r.   r   r   r   r/   r0   r1   r2   r3   r4   r5   rJ   rK   s                    r6   ;test_scan_copies_to_incoming_while_notified_marker_coexistsz]TestManualAndAutoChainCoexistence.test_scan_copies_to_incoming_while_notified_marker_coexists  s   (
*, ;.*
8	j	 ,,V4	o	%11*=!#j/3|3Dc-FXYV#RRRzVRRRRRRzRRRzRRRRRRVRRRVRRRR%RRRRRRRz)Z)11Z13Z3ZZ5ZZZZZZZZZZZZZZZZzZZZzZZZ1ZZZ3ZZZZZZf,ZZZfZZZZZZZZZZZZZZZfZZZfZZZZ.ZZZZZZZ ?2h2::h:<h<<h<hh>hhhhhhhLhhhLhhhhhh?hhh?hhh:hhh<hhhhhhr8   c                    |dz  }|dz  }|dz  }|j                          |j                          |j                          d}d}||z  j                  |       t        t        |      t        |      t        |             ||z  }|j                  } |       }	|	st        j                  d      dz   dt        j                         v st        j                  |      rt        j                  |      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}x}}	||z  }|j                  } |       }	|	|k(  }|sEt        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	t        j                  |      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}x}x}	}||z  }|j                  } |       }	|	st        j                  d      dz   dt        j                         v st        j                  |      rt        j                  |      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}x}}	y)uj   scan 후 원본 .done이 보존되어 chain_manager와 auto_orch 양쪽 모두 사용 가능해야 한다.r	   r
   r   zpipeline-multi-consumer.donezpipeline_id=multi-consumeru5   원본 .done이 삭제되어 Layer 2 경로 차단됨r   r-   r   r   Nr\   )zT%(py6)s
{%(py6)s = %(py4)s
{%(py4)s = (%(py0)s / %(py1)s).read_text
}()
} == %(py8)soriginal_content)r   r   r   r   rc   u    원본 .done 내용이 변경됨rt   ru   u/   Layer 3 경로용 incoming/ 복사본이 없음r   )r   r   r   r    r*   r!   r'   r#   r$   r%   r&   r(   r)   	read_textr"   )r+   r,   r-   r   r.   r   r   r2   r3   r4   r5   rJ   re   rx   s                 r6   6test_original_done_available_for_both_paths_after_scanzXTestManualAndAutoChainCoexistence.test_original_done_available_for_both_paths_after_scan&  s|   (
*, ;.3
7	j	 ,,-=>Z#l*;S=OP Z'j'//j/1j1jj3jjjjjjj
jjj
jjjjjjZjjjZjjj/jjj1jjjjjjZ'l'22l24l48HHlll48Hllllll
lll
llllllZlllZlll2lll4llllll8Hlll8HllllJllllllll z)f)11f13f3ff5ffffffffffffffffzfffzfff1fff3ffffffr8   c                 H   |dz  }|dz  }|dz  }|j                          |j                          |j                          |dz  j                  d       t        t        |      t        |      t        |            }t	        |      }d}||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                  |      t        j                  |      dz  }	t        j                  d      dz   d|	iz  }
t        t        j                  |
            dx}x}}y)ub   .done.notified 마커가 있어도 scan_done_events 결과가 .done 파일만 포함해야 한다.r	   r
   r   z$pipeline-notified-only.done.notifiedr   r   r\   r^   r_   r   r`   u7   .done.notified만 있는데 scan이 파일을 복사함rb   rc   Nr   r   r   r    r_   r!   r"   r#   r$   r%   r&   r'   r(   r)   )r+   r,   r-   r   r.   r   r2   r4   rd   r5   re   s              r6   0test_notified_marker_does_not_affect_scan_resultzRTestManualAndAutoChainCoexistence.test_notified_marker_does_not_affect_scan_result<  s   (
*, ;. 
<	<HHT!#j/3|3Dc-FXY6{ZaZ{aZZZ{aZZZZZZsZZZsZZZZZZ6ZZZ6ZZZ{ZZZaZZZ!ZZZZZZZZr8   N)rO   rP   rQ   rR   r   r   r   rS   r8   r6   r   r     s    ki0g,[r8   r   c                   "    e Zd ZdZd Zd Zd Zy)TestDoneClearRenameuD   T5: .done → .done.clear rename 후 scan이 무시하는지 확인.c                    |dz  }|dz  }|dz  }|j                          |j                          |j                          d}||z  j                  d       t        t        |      t        |      t        |            }||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z   d|iz  }	t        t	        j                  |	            d}||z  }
|
j                  } |       }| }|st	        j                  d      dz   dt        j                         v st	        j                  |      rt	        j                  |      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}
x}x}}y)u\   .done.clear 파일은 *.done 패턴이 아니므로 scan_done_events가 무시해야 한다.r	   r
   r   zpipeline-003.done.clearclearedrX   rZ   cleared_filer   r   u.   .done.clear 파일이 scan 결과에 포함됨r   r   Nu,   .done.clear 파일이 incoming/에 복사됨rF   r   r   r   )r+   r,   r-   r   r.   r   r   r/   r0   r1   r2   r3   r4   rJ   rK   s                  r6   "test_done_clear_is_ignored_by_scanz6TestDoneClearRename.test_done_clear_is_ignored_by_scanU  s   (
*, ;.0	l	"..y9!#j/3|3Dc-FXY6)[[[|6[[[[[[|[[[|[[[[[[6[[[6[[[[+[[[[[[[ </i/77i79i99i9ii;iiiiiiiLiiiLiiiiii<iii<iii7iii9iiiiiir8   c                    |dz  }|dz  }|dz  }|j                          |j                          |j                          d}d}||z  j                  d       t        t        |      t        |      t        |            }||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z   d|	iz  }
t        t	        j                  |
            d}||z  j                  ||z         ||z  }|j                  } |       }| }|st	        j                  d      dz   dt        j                         v st	        j                  |      rt	        j                  |      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}x}x}}t        t        |      t        |      t        |            }t        |      }d}||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                  |      t	        j                  |      dz  }t	        j                  d|       dz   d|iz  }t        t	        j                  |            dx}x}}y)uM   scan → .done.clear rename → 재scan 시 재처리 없음을 확인한다.r	   r
   r   zpipeline-anu.donezpipeline-anu.done.clearr   r   r   r   rW   r   u#   첫 번째 scan에서 복사 실패r   r   Nu#   .done → .done.clear rename 실패rF   r-   r   r   r\   r^   r_   r[   r`   u;   rename 후 재scan에서 불필요한 복사가 발생함: rb   rc   )r   r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   renamer*   r_   )r+   r,   r-   r   r.   r   
clear_filerW   r/   r0   r1   r2   r3   r4   rJ   rK   r[   rd   r5   re   s                       r6   0test_scan_then_rename_to_clear_then_no_reprocesszDTestDoneClearRename.test_scan_then_rename_to_clear_then_no_reprocessf  so   (
*, ;.(
.
	j	 ,,V4 (J\9JCP]L^_\)PPPz\PPPPPPzPPPzPPPPPP\PPP\PPPP+PPPPPPP 
j	 ((j)@A+\+33\35\55\5\\7\\\\\\\J\\\J\\\\\\\\\\\\3\\\5\\\\\\ )Z#l:KSQ^M_`=!uQu!Q&uuu!Quuuuuusuuusuuuuuu=uuu=uuu!uuuQuuu*efset(uuuuuuuur8   c                    |dz  }|dz  }|dz  }|j                          |j                          |j                          |dz  j                  d       |dz  j                  d       t        t        |      t        |      t        |            }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}}y)uF   task-123.done.clear 형식은 scan_done_events가 무시해야 한다.r	   r
   r   task-123.done.clearr   zpipeline-valid.doner   rX   z%(py1)s not in %(py3)sr   r   rC   u/   task-123.done.clear가 scan 결과에 포함됨rD   rE   Nr   z%(py1)s in %(py3)su/   유효한 .done 파일이 scan 결과에 없음)r   r   r   r    r!   r"   r&   r#   r$   r%   r'   r(   r)   )
r+   r,   r-   r   r.   r   @py_assert0r2   rH   rI   s
             r6   /test_invalid_pattern_task_done_clear_is_ignoredzCTestDoneClearRename.test_invalid_pattern_task_done_clear_is_ignored  sX   (
*, ;.	+	+77	B	+	+77?!#j/3|3Dc-FXY$e$F2eee$Feee$eeeeeeFeeeFeeee4eeeeeee$a$.aaa$aaa$aaaaaaaaaaaaa0aaaaaaar8   N)rO   rP   rQ   rR   r   r   r   rS   r8   r6   r   r   R  s    Nj"v4br8   r   c                       e Zd ZdZd Zd Zy)TestSymlinkRejectionu4   scan_done_events의 symlink 보안 거부 테스트.c                    |dz  }|dz  }|dz  }|j                          |j                          |j                          |dz  }|j                  d       |dz  }|j                  |       |j                  } |       }|st	        j
                  d      dz   d	t        j                         v st	        j                  |      rt	        j                  |      nd	t	        j                  |      t	        j                  |      d
z  }	t        t	        j                  |	            dx}}t        t        |      t        |      t        |            }
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}||z  }|j                  } |       }| }|st	        j
                  d      dz   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}x}x}}y)uh   events/의 .done 파일이 symlink이면 scan_done_events가 무시하고 복사하지 않아야 한다.r	   r
   r   z	real.doner   pipeline-symlink.doneu   symlink 생성 실패zG
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.is_symlink
}()
}symlink_file)r   r   r   NrX   r   r   r   uI   symlink .done 파일이 scan 결과에 포함됨 — 보안 거부 실패rD   rE   u@   symlink .done이 incoming/에 복사됨 — 보안 거부 실패zS
>assert not %(py7)s
{%(py7)s = %(py5)s
{%(py5)s = (%(py0)s / %(py2)s).exists
}()
}r   )r   r   rE   py7)r   r   
symlink_to
is_symlinkr!   r'   r#   r$   r%   r&   r(   r)   r   r    r"   r*   )r+   r,   r-   r   r.   	real_filer   r/   r3   r1   r   r   r2   rH   rI   rd   @py_assert6@py_assert8re   s                      r6   #test_scan_rejects_symlink_done_filez8TestSymlinkRejection.test_scan_rejects_symlink_done_file  s   (
*, ;.{*	V$!$;;	*&&A&(A(AA*AAAAAAA|AAA|AAA&AAA(AAAAAA!#j/3|3Dc-FXY&  	B&f4  	B  	B  	B&f  	B  	B  	B&  	B  	B  	B  	B  	B  	Bf  	B  	B  	Bf  	B  	B  	B  	B  7B  	B  	B  	B  	B  	B  	B2	W22	W2
&	W   	W   	W  	WEVEVV	W 	WPVPV	W 	W>V>V 	W 	WMVY 	W 	WMVY 3	W 	WMVY 	W 	WMVY 	W 	W 	WCVCV	W 	W 	Wr8   c                 :   |dz  }|dz  }|dz  }|j                          |j                          |j                          |dz  j                  d       |dz  }|j                  d       |dz  j                  |       t        t	        |      t	        |      t	        |            }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}}y)uL   실제 .done 파일은 복사하고 symlink .done은 건너뛰어야 한다.r	   r
   r   zpipeline-real.doner   zreal_target.doner   r   r   r   r   u.   실제 .done 파일이 복사 목록에 없음rD   rE   NrX   r   u2   symlink .done 파일이 복사 목록에 포함됨)r   r   r   r   r    r!   r"   r&   r#   r$   r%   r'   r(   r)   )r+   r,   r-   r   r.   r   r   r   r2   rH   rI   s              r6   ,test_scan_copies_real_file_but_skips_symlinkzATestSymlinkRejection.test_scan_copies_real_file_but_skips_symlink  su   (
*, ;. 
*	*66v> 11	V$	-	-99)D!#j/3|3Dc-FXY#_#v-___#v___#______v___v____/_______&j&f4jjj&fjjj&jjjjjjfjjjfjjjj6jjjjjjjr8   N)rO   rP   rQ   rR   r   r   rS   r8   r6   r   r     s    >W.kr8   r   c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestEdgeCasesuP   빈 디렉토리, 미존재 디렉토리, 잘못된 패턴 등 엣지 케이스.c                 R   |dz  }|dz  }|dz  }|j                          |j                          |j                          t        t        |      t        |      t        |            }g }||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	      d
z   d|iz  }	t        t        j                  |	            dx}}y)ub   events_dir에 .done 파일이 없을 때 scan_done_events는 빈 리스트를 반환해야 한다.r	   r
   r   r\   z%(py0)s == %(py3)sr   rB   u/   빈 디렉토리인데 빈 리스트가 아닌    를 반환함rD   rE   Nr   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   
r+   r,   r-   r   r.   r   r2   r/   rH   rI   s
             r6   (test_empty_events_dir_returns_empty_listz6TestEdgeCases.test_empty_events_dir_returns_empty_list  s    (
*, ;.!#j/3|3Dc-FXYdv|dddvddddddvdddvddddddNvhVcdddddddr8   c                 2   |dz  }|dz  }|dz  }|j                          |j                          t        t        |      t        |      t        |            }g }||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	      d
z   d|iz  }	t        t        j                  |	            dx}}y)um   events_dir가 존재하지 않을 때 scan_done_events는 예외 없이 빈 리스트를 반환해야 한다.nonexistent_eventsr
   r   r\   r   r   rB   u5   미존재 디렉토리인데 빈 리스트가 아닌 r   rD   rE   Nr   r   s
             r6   .test_nonexistent_events_dir_returns_empty_listz<TestEdgeCases.test_nonexistent_events_dir_returns_empty_list  s     44
*, ;.!#j/3|3Dc-FXYjv|jjjvjjjjjjvjjjvjjjjjjTU[T\\ijjjjjjjr8   c                 V   |dz  }|dz  }|dz  }|j                          |j                          |j                          t        t        |      t        |      t        |            }t        |t              }|s-t        j                  dt        |             dz   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
)u7   scan_done_events는 항상 list를 반환해야 한다.r	   r
   r   u2   scan_done_events 반환 타입이 list가 아님: z7
>assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstancerA   list)r   r   r   r   N)r   r   r    r   r   r!   r'   typer#   r$   r%   r&   r(   r)   )r+   r,   r-   r   r.   rA   r3   r1   s           r6   test_scan_returns_list_typez)TestEdgeCases.test_scan_returns_list_type  s   (
*, ;.!#j/3|3Dc-FXY&$'l'll+]^bci^j]k)lllllllzlllzllllll&lll&llllll$lll$lll'llllllr8   c                 x   |dz  }|dz  }|dz  }|j                          |j                          |j                          |dz  j                  d       |dz  j                  d       |dz  j                  d       |dz  j                  d	       |d
z  j                  d       t        t        |      t        |      t        |            }t	        |      }d}||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                  |      t        j                  |      dz  }	t        j                  dt	        |       d      dz   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  }t        j                  d      dz   d|iz  }t        t        j                  |            dx}}y)uY   .txt, .yaml, .json 등 .done이 아닌 파일은 scan_done_events가 무시해야 한다.r	   r
   r   zpipeline-001.txtznot donezpipeline-001.yamlzpipeline-001.jsonREADMEreadmer   r      r\   r^   r_   r   r`   u6   유효한 .done 파일이 1개인데 복사 건수가 ra   rb   rc   Nr   r   r   u'   .done 파일이 복사 목록에 없음rD   rE   r   )r+   r,   r-   r   r.   r   r2   r4   rd   r5   re   r   rH   rI   s                 r6   )test_non_done_extension_files_are_ignoredz7TestEdgeCases.test_non_done_extension_files_are_ignored  s   (
*, ;.	(	(44Z@	)	)55jA	)	)55jA	h	**84	)	)55f=!#j/3|3Dc-FXY6{mam{ammm{ammmmmmsmmmsmmmmmm6mmm6mmm{mmmammm#YZ]^dZeYffl!mmmmmmmm"W"f,WWW"fWWW"WWWWWWfWWWfWWWW.WWWWWWWr8   c                 |   |dz  }|dz  }|dz  }|j                          |j                          |j                          g d}|D ]  }||z  j                  d        t        t        |      t        |      t        |            }t	        |      }d}	||	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                  |      t        j                  |	      dz  }t        j                  dt	        |       d|       dz   d|iz  }t        t        j                  |            dx}x}
}	y)ue   *.done.clear, *.done.notified 등 확장자 패턴이 *.done과 다른 파일은 무시해야 한다.r	   r
   r   )zpipeline-001.done.clearzpipeline-002.done.notifiedzpipeline-003.done.bakr   invalidr   r\   r^   r_   r   r`   u!   잘못된 패턴 파일들인데 u   건이 복사됨: rb   rc   Nr   )r+   r,   r-   r   r.   invalid_patternsfnamer   r2   r4   rd   r5   re   s                r6   #test_done_clear_pattern_not_matchedz1TestEdgeCases.test_done_clear_pattern_not_matched
  s:   (
*, ;.
 & 	7E%++I6	7 "#j/3|3Dc-FXY6{lal{alll{allllllslllsllllll6lll6lll{lllalll#DS[MQcdjck!llllllllr8   N)	rO   rP   rQ   rR   r   r   r   r   r   rS   r8   r6   r   r     s#    Ze
kmX(mr8   r   c                   "    e Zd ZdZd Zd Zd Zy)TestConsumeEventIndependenceue   consume_event의 기존 동작이 scan_done_events 도입 후에도 그대로 유지되는지 확인.c                 d   |dz  }|dz  }|j                          |j                          |dz  }|j                  d       |dz  }|j                  |       t        t	        |      t	        |      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  }	t        j                  d      dz   d|	iz  }
t        t        j                  |
            dx}}y)uS   scan 도입 후에도 consume_event는 incoming/의 symlink를 거부해야 한다.r
   r   zreal_event.doner   r   Fr>   r@   rA   rB   uH   consume_event가 symlink를 거부하지 않음 — 보안 거부 실패rD   rE   N)r   r   r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   )r+   r,   r   r.   r   symlink_in_incomingrA   r2   r/   rH   rI   s              r6   4test_consume_event_still_rejects_symlink_in_incomingzQTestConsumeEventIndependence.test_consume_event_still_rejects_symlink_in_incoming)  s    *, ;.00	V$*-DD&&y1s<0#m2DF]^jvjjjvjjjjjjvjjjvjjjjjj jjjjjjjr8   c                    |dz  }|dz  }|dz  }|j                          |j                          |j                          d}||z  j                  d       t        t        |      t        |      t        |             t	        t        |      t        |      |      }d}||u }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
z  }	t        j                  d      dz   d|	iz  }
t        t        j                  |
            dx}}||z  }|j                  } |       }| }|st        j                  d      dz   dt        j                         v st        j                  |      rt        j                  |      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}x}x}}||z  }|j                  } |       }|st        j                  d      dz   dt        j                         v st        j                  |      rt        j                  |      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}x}}y)uf   scan으로 복사된 파일에 대해 consume_event의 원자적 rename이 정상 동작해야 한다.r	   r
   r   zpipeline-atomic.doner   Tr>   r@   rA   rB   u   원자적 rename이 실패함rD   rE   Nu.   rename 후 incoming/에 파일이 남아있음rF   r   r   r   u)   rename 후 processed/에 파일이 없음r   r.   rG   )r+   r,   r-   r   r.   r   rA   r2   r/   rH   rI   r3   r4   rJ   rK   r5   s                   r6   ,test_consume_event_atomic_rename_still_workszITestConsumeEventIndependence.test_consume_event_atomic_rename_still_works9  s    (
*, ;.+
	j	 ,,V4Z#l*;S=OPs<0#m2DjQ>v~>>>v>>>>>>v>>>v>>>>>>>>>>>>> :-i-55i57i77i7ii9iiiiiiiLiiiLiiiiii:iii:iii5iii7iiiiii
*a*22a24a4aa6aaaaaaaaaaaaaaaa
aaa
aaa2aaa4aaaaaar8   c                    |dz  }|dz  }|j                          |j                          t        t        |      t        |      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  }t        j                  d	      d
z   d|iz  }t        t        j                  |            dx}}y)ue   scan 없이 incoming/에 파일이 없는 상태에서 consume_event는 False를 반환해야 한다.r
   r   zpipeline-not-there.doneFr>   r@   rA   rB   uC   incoming/에 없는 파일인데 consume_event가 True를 반환함rD   rE   N)r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   )	r+   r,   r   r.   rA   r2   r/   rH   rI   s	            r6   :test_consume_event_returns_false_when_file_not_in_incomingzWTestConsumeEventIndependence.test_consume_event_returns_false_when_file_not_in_incomingL  s    *, ;.s<0#m2DF_`eveeeveeeeeeveeeveeeeee eeeeeeer8   N)rO   rP   rQ   rR   r   r   r   rS   r8   r6   r   r   &  s    ok b&	fr8   r   )rR   builtinsr#   _pytest.assertion.rewrite	assertionrewriter!   ossyspathlibr   pytestWORKSPACE_ROOTpathinsertorchestrator.event_busr   r   r   rU   rq   r   r   r   r   r   rS   r8   r6   <module>r      s     	 
  
 '!HHOOA~&
 CHm Hm`Cm CmVAq AqR?[ ?[N=b =bJ.k .klQm Qmr/f /fr8   