
    L.i'j                        U 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ZddlZddlmZ ddlmZmZ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mZmZmZmZmZm Z m!Z!m"Z"m#Z#m$Z$m%Z% ddl&m'Z' dd	d
ddgdddddddgddddddddddddddgddgd 	Z(e)e*d!<   d"e+d#e,fd$Z- G d% d&      Z. G d' d(      Z/ G d) d*      Z0 G d+ d,      Z1 G d- d.      Z2 G d/ d0      Z3 G d1 d2      Z4y)3u   TDD RED Phase 3 테스트 모음.

대상 모듈 (아직 미구현):
  - orchestrator.team_lock   : fcntl.flock 기반 팀별 배타적 접근 제어
  - orchestrator.auto_orch   : CLI 오케스트레이터 핵심 함수들

작성자 : 헤임달 (dev2-team tester)
날짜   : 2026-03-24
    N)Path)	MagicMockcallpatchz/home/jay/workspace)acquire_global_lockcmd_listcmd_runcmd_scan
cmd_statuscmd_validatedispatch_stepget_pipeline_stateload_pipelinerelease_global_locksave_pipeline_statescan_eventsupdate_healthTeamLock1.0test-pipeline-001zTest Pipeline Phase 3	dev1-team	dev2-teami@  teamzgate-1	qc_review
qc_officer0   )idtypeapprover_roletimeout_hoursTpipeline-001.done)manualeventstep-1zStep 1pipelines/templates/test.md<   r   nametarget_teamtask_file_templatetimeout_minuteszstep-2zStep 2zpipelines/templates/test2.md)r   r)   r*   r+   
depends_onr,   )	schema_versionr   r)   allowed_teamstoken_budgetblast_radiusgatestriggersstepsVALID_PIPELINEargsreturnc                    | \  }}}ddl }t        |j                  vr |j                  j                  dt               ddlm} 	  |||      5  ddl}|j                  d       ddd       d}|S # 1 sw Y   xY w# t        $ r d}Y |S w xY w)uF   multiprocessing 워커: TeamLock 획득 시도 결과를 반환한다.r   Nr   	locks_dirg333333?TF)	sysWORKSPACE_ROOTpathinsertorchestrator.team_lockr   timesleepBlockingIOError)r6   r:   team_idresult_queue_pathr;   _TLr@   acquireds           5/home/jay/workspace/orchestrator/tests/test_phase3.py_worker_try_acquirerH   ^   s    ,0)Iw)SXX%>*6I. 	JJsO		
  O	 	  Os*   
A< A0$
A< 0A95A< <B
Bc                   :    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
y	)
TestTeamLocku'   TeamLock 클래스 테스트 스위트.c                    t        d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}}y)uR   TeamLock(team_id, locks_dir)으로 인스턴스를 생성할 수 있어야 한다.r   r9   Nis notz%(py0)s is not %(py3)slockpy0py3u#   TeamLock 인스턴스 생성 실패
>assert %(py5)spy5r   str
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgAssertionError_format_explanation)selftmp_pathrO   @py_assert2@py_assert1@py_format4@py_format6s          rG   test_teamlock_instantiationz(TestTeamLock.test_teamlock_instantiationv   st    s8}=Ft4FFFt4FFFFFFtFFFtFFF4FFF!FFFFFFF    c                 2   t        dt        |            5 }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}}ddd       |dz  }t        dt        |            5  	 ddd       y# 1 sw Y   /xY w# 1 sw Y   yxY w)uU   TeamLock을 context manager로 사용하면 획득 후 정상 해제되어야 한다.r   r9   NrL   rN   tlrP   u   __enter__ 반환값이 None임rS   rT   zdev2-team.lockrU   )r`   ra   ri   rb   rc   rd   re   	lock_files           rG   3test_teamlock_context_manager_acquires_and_releasesz@TestTeamLock.test_teamlock_context_manager_acquires_and_releases{   s    kS]; 	Dr!C2T>CCC2TCCCCCC2CCC2CCCTCCC#CCCCCCC	D //	kS]; 		 		D 	D
	 	s   B<D6DD
Dc                    d}t        |t        |            5  || dz  }|j                  } |       }|st        j                  d| 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}}d	d	d	       y	# 1 sw Y   y	xY w)
uQ   TeamLock 획득 시 locks_dir에 <team_id>.lock 파일이 생성되어야 한다.r   r9   z.locku   락 파일 u   이 생성되지 않음C
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}rj   rQ   py2py4N)r   rV   existsrW   r]   rY   rZ   r[   r\   r^   r_   )r`   ra   rC   rj   rc   @py_assert3@py_format5s          rG   test_teamlock_creates_lock_filez,TestTeamLock.test_teamlock_creates_lock_file   s    gX7 	X gYe#44I##W#%W%WWYK?V'WWWWWWW9WWW9WWW#WWW%WWWWWW	X 	X 	Xs   C	C++C4c                    t        j                  d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}}y)uE   락이 없을 때 is_team_available()은 True를 반환해야 한다.z	free-teamr9   Tisz%(py0)s is %(py3)sresultrP   u?   락이 없는 팀인데 is_team_available이 False를 반환함rS   rT   N)r   is_team_availablerV   rW   rX   rY   rZ   r[   r\   r]   r^   r_   )r`   ra   ry   rb   rc   rd   re   s          rG   1test_is_team_available_returns_true_when_unlockedz>TestTeamLock.test_is_team_available_returns_true_when_unlocked   sy    ++K3x=Q`v~```v``````v```v`````````````rg   c                    d}t        j                  d      }d }t        j                  |t        |      ||f      }|j	                          |j                          t        j                  |t        |            }|j                          |j                  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)uh   다른 프로세스가 팀 락을 보유 중일 때 is_team_available()은 False를 반환해야 한다.z	busy-team   c                     dd l }t        |j                  vr |j                  j                  dt               ddlm}  |||       5  |j                          |j                          d d d        y # 1 sw Y   y xY w)Nr   r   r9   )r;   r<   r=   r>   r?   r   wait)r:   tidbarrierr;   rE   s        rG   	hold_lockzPTestTeamLock.test_is_team_available_returns_false_when_locked.<locals>.hold_lock   sW    SXX->2>SI.   s   !A11A:)targetr6   r9      )timeoutFrv   rx   ry   rP   uB   락 보유 중인 팀인데 is_team_available이 True를 반환함rS   rT   N)multiprocessingBarrierProcessrV   startr   r   rz   joinrW   rX   rY   rZ   r[   r\   r]   r^   r_   )r`   ra   rC   r   r   pry   rb   rc   rd   re   s              rG   0test_is_team_available_returns_false_when_lockedz=TestTeamLock.test_is_team_available_returns_false_when_locked   s     "))!,		 ##9CM7T[;\]		++Gs8}M	qdvdddvddddddvdddvdddddd dddddddrg   c                    d}t        |      |df}t        j                  d      5 }|j                  t        ||g      }ddd       j                  d      }|j                  d      }d}||k(  }	|	st        j                  d|	fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      dz  }
t        j                  d|       dz   d|
iz  }t        t        j                  |            dx}	}d}||k(  }	|	st        j                  d|	fd	||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }
t        j                  d|       dz   d|
iz  }t        t        j                  |            dx}	}y# 1 sw Y   xY w)uh   2개 프로세스가 동시에 동일 팀 락 획득 시도 → 1개만 성공, 1개는 BlockingIOError.zcontested-teamNr}   )	processesTF   ==)z%(py0)s == %(py3)s
true_countrP   u9   정확히 1개 프로세스만 True여야 하는데 True=rS   rT   false_countu;   정확히 1개 프로세스만 False여야 하는데 False=)rV   r   PoolmaprH   countrW   rX   rY   rZ   r[   r\   r]   r^   r_   )r`   ra   rC   r6   poolresultsr   r   rb   rc   rd   re   s               rG   )test_two_processes_only_one_acquires_lockz6TestTeamLock.test_two_processes_only_one_acquires_lock   sJ   " Hw-!!A. 	B$hh2T4LAG	B ]]4(
mmE*hzQhhhzQhhhhhhzhhhzhhhQhhh"[\f[g hhhhhhhl{alll{allllll{lll{lllalll#^_j^k!lllllll	B 	Bs   G))G3c                     t        dt        |            5  t        dt        |            5  	 ddd       ddd       y# 1 sw Y   xY w# 1 sw Y   yxY w)u>   서로 다른 팀 락은 동시에 획득 가능해야 한다.r   r9   r   N)r   rV   )r`   ra   s     rG   <test_teamlock_different_teams_can_be_acquired_simultaneouslyzITestTeamLock.test_teamlock_different_teams_can_be_acquired_simultaneously   sP    kS]; 	+X? 	 	 	 	s!   AAAA
	AAN)__name__
__module____qualname____doc__rf   rk   rt   r{   r   r   r    rg   rG   rJ   rJ   s   s-    1G
Xa
e4mrg   rJ   c                   "    e Zd ZdZd Zd Zd Zy)TestGlobalLocku>   acquire_global_lock / release_global_lock 테스트 스위트.c                    t        |dz        }|j                  d|       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}}t        |t              }	|	s.t        j                  dt        |       d      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}	|t        |       yy# |t        |       w w xY w)uB   acquire_global_lock() 첫 호출은 int fd를 반환해야 한다.ztest-auto-orch.lock'orchestrator.auto_orch.GLOBAL_LOCK_PATHNrL   rN   fdrP   5   첫 번째 acquire_global_lock()이 None을 반환함rS   rT   u'   acquire_global_lock()이 int가 아닌    를 반환함7
>assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstanceintrQ   py1ro   rp   )rV   setattrr   rW   rX   rY   rZ   r[   r\   r]   r^   r_   r   r   r   r   )r`   ra   monkeypatch	lock_pathr   rb   rc   rd   re   rr   rs   s              rG   #test_acquire_global_lock_returns_fdz2TestGlobalLock.test_acquire_global_lock_returns_fd   s\   #889	EyQ "	(!Z2T>ZZZ2TZZZZZZ2ZZZ2ZZZTZZZ#ZZZZZZZb#&i&ii*QRVWYRZQ[[h(iiiiiii:iii:iiiiiibiiibiiiiii#iii#iii&iiiiii~#B' r~#B' s   G>H9 9I	c                    t        |dz        }|j                  d|       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}}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      d	z   d
|iz  }t        t        j                  |            dx}}|t        |       yy# |t        |       w w xY w)u\   acquire_global_lock() 두 번째 호출 → None을 반환해야 한다 (이미 락 보유).ztest-auto-orch2.lockr   NrL   rN   fd1rP   r   rS   rT   rv   rx   fd2u3   두 번째 acquire_global_lock()이 None이 아닌 r   )rV   r   r   rW   rX   rY   rZ   r[   r\   r]   r^   r_   r   
r`   ra   r   r   r   rb   rc   rd   re   r   s
             rG   1test_acquire_global_lock_second_call_returns_nonez@TestGlobalLock.test_acquire_global_lock_second_call_returns_none   s+   #99:	EyQ!#	)"[3d?[[[3d[[[[[[3[[[3[[[d[[[$[[[[[[[%'Ch3$;hhh3$hhhhhh3hhh3hhh$hhh"UVYUZZg hhhhhhh#C( s#C( s   FF? ?Gc                    t        |dz        }|j                  d|       t               }d}||u}|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd	|iz  }t        t        j                  |            dx}}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}}|	t        |	       yy# |	t        |	       w w xY w)uX   release_global_lock() 후 동일 경로의 락을 다시 획득할 수 있어야 한다.ztest-auto-orch3.lockr   NrL   rN   r   rP   zassert %(py5)srT   r   u-   락 해제 후 재획득이 None을 반환함rS   )rV   r   r   rW   rX   rY   rZ   r[   r\   r^   r_   r   r]   r   s
             rG   )test_release_global_lock_allows_reacquirez8TestGlobalLock.test_release_global_lock_allows_reacquire   s   #99:	EyQ!#s$s$ss$C !#	)"S3d?SSS3dSSSSSS3SSS3SSSdSSS$SSSSSSS#C( s#C( s   &B;F0 0G N)r   r   r   r   r   r   r   r   rg   rG   r   r      s    H
())rg   r   c                   4    e Zd ZdZd Zd Zd Zd Zd Zd Z	y)	TestAutoOrchValidateu1   load_pipeline + cmd_validate 테스트 스위트.c                    ddl }|dz  }|j                  |j                  t                     t	        t        |            }t        |t              }|s!t        j                  d      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}|j                   }d
} ||      }d}	||	k(  }
|
st        j"                  d|
fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}x}x}
}	y)uV   유효한 YAML 파일을 load_pipeline으로 로드하면 dict를 반환해야 한다.r   Nzvalid_pipeline.yamlu1   유효한 YAML인데 dict가 아닌 타입 반환r   r   ry   dictr   r.   r   r   zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} == %(py9)srQ   ro   rp   py6py9zassert %(py11)spy11)yaml
write_textdumpr5   r   rV   r   r   rW   r]   rY   rZ   r[   r\   r^   r_   getrX   )r`   ra   r   pipeline_filery   rr   rs   rc   @py_assert5@py_assert8@py_assert7@py_format10@py_format12s                rG   test_load_pipeline_valid_yamlz2TestAutoOrchValidate.test_load_pipeline_valid_yaml   se    #88  >!:;s=12&$'\'\\)\\\\\\\z\\\z\\\\\\&\\\&\\\\\\$\\\$\\\'\\\\\\zz4*4z*+4u4+u4444+u444444v444v444z444*444+444u44444444rg   c                 "   ddl }t        j                  t              }|d= |dz  }|j	                  |j                  |             t        t        |            }t        |t              }|s!t        j                  d      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)uj   schema_version 없는 YAML은 load_pipeline이 에러를 보고하거나 예외를 발생시켜야 한다.r   Nr.   zinvalid_pipeline.yamlu,   load_pipeline이 dict를 반환하지 않음r   r   ry   r   r   )r   copydeepcopyr5   r   r   r   rV   r   r   rW   r]   rY   rZ   r[   r\   r^   r_   )r`   ra   r   invalidr   ry   rr   rs   s           rG   :test_load_pipeline_missing_schema_version_raises_or_errorszOTestAutoOrchValidate.test_load_pipeline_missing_schema_version_raises_or_errors  s    --/$% #::  7!34 s=12&$'W'WW)WWWWWWWzWWWzWWWWWW&WWW&WWWWWW$WWW$WWW'WWWWWWrg   c                     |dz  }|j                  d       t        j                  t              5  t	        t        |             ddd       y# 1 sw Y   yxY w)uY   YAML 구문 오류가 있는 파일은 load_pipeline이 예외를 발생시켜야 한다.zbad.yamlzkey: [unclosed bracket
foo: barN)r   pytestraises	Exceptionr   rV   )r`   ra   bad_yamls      rG   -test_load_pipeline_invalid_yaml_syntax_raiseszBTestAutoOrchValidate.test_load_pipeline_invalid_yaml_syntax_raises  sH    j(>?]]9% 	)#h-(	) 	) 	)s   AAc                    ddl }|dz  }|j                  |j                  t                     t	        t        |             |j                         }|j                  |j                  z   j                         }g }d}||v}	|	}
|	s&d}||v }|}
|sd}||v }|}
|sd}||v }|}
|sd}||v }|}
|
s4t        j                  d	|	fd
||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }|j!                  |       |	s8t        j                  dfd|f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }|j!                  |       |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  }|j!                  |       |st        j                  dfd|f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }|j!                  |       |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  }|j!                  |       t        j"                  |d       i z  }t        j$                  d!|j                   |j                         d"z   d#|iz  }t'        t        j(                  |            dx}
x}x}x}	x}x}x}x}x}x}x}}y)$uN   유효한 YAML에 대해 cmd_validate는 에러 없음을 출력해야 한다.r   Nz
valid.yamlerrorz0 errorvalidokpassnot in)z%(py3)s not in %(py5)soutputrR   rT   %(py7)spy7in)z%(py10)s in %(py12)s)py10py12z%(py14)spy14)z%(py17)s in %(py19)s)py17py19z%(py21)spy21)z%(py24)s in %(py26)s)py24py26z%(py28)spy28)z%(py31)s in %(py33)s)py31py33z%(py35)spy35r   u+   유효한 YAML인데 에러 출력 발생: z
>assert %(py38)spy38)r   r   r   r5   r   rV   
readouterrouterrlowerrW   rX   r\   rY   rZ   r[   append_format_boolopr]   r^   r_   )r`   ra   capsysr   r   capturedr   rc   rb   @py_assert4@py_assert0@py_assert9@py_assert11@py_assert16@py_assert18@py_assert23@py_assert25@py_assert30@py_assert32re   @py_format8@py_format13@py_format15@py_format20@py_format22@py_format27@py_format29@py_format34@py_format36@py_format37@py_format39s                                  rG   -test_cmd_validate_valid_yaml_outputs_no_errorzBTestAutoOrchValidate.test_cmd_validate_valid_yaml_outputs_no_error  s
    </  >!:;S'($$&,,-446	V	V6!	V%.	V%.&%8	V<C	V<Cv<M	VQU	VQUY_Q_	Vci	Vcimscs	V 	VDUDU	V6	V 	VLUI 	V 	VOUv	V 	V=U=U "	V 	VLUI "	V 	V 	VOUv	VOU	VDUDU	V%.&	V 	VLUI &/	V 	VOUv	V 	V=U=U 39	V 	VLUI 39	V 	V 	VOUv	VOU	VDUDU	V<Cv	V 	VLUI =D	V 	VOUv	V 	V=U=U HN	V 	VLUI HN	V 	V 	VOUv	VOU	VDUDU	VQUY_	V 	VLUI RV	V 	VOUv	V 	V=U=U Z`	V 	VLUI Z`	V 	V 	VOUv	VOU	VDUDU	Vcims	V 	VLUI dj	V 	VOUv	V 	V=U=U nt	V 	VLUI nt	V 	V 	VOUv	VOU	VGU~	V 	VDUDU8x||nU	V 	V 	VBUBU	V 	V 	V 	V 	Vrg   c           	      	   ddl }t        j                  t              }|d= |dz  }|j	                  |j                  |             t        t        |             |j                         }|j                  |j                  z   }|j                  } |       }	t        |	      }
d}|
|kD  }|s4t        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$                  |	      t        j$                  |
      t        j$                  |      d	z  }t        j&                  d
      dz   d|iz  }t)        t        j*                  |            dx}x}	x}
x}}g }d}|j,                  }
 |
       }||v }	|	}|	s8d}|j,                  } |       }||v }|}|sd}|j,                  } |       }||v }|}|sxt        j                  d|	fd||f      t        j$                  |      dt        j                          v st        j"                  |      rt        j$                  |      ndt        j$                  |
      t        j$                  |      dz  }dd|iz  }|j/                  |       |	sjt        j                  dfdf      t        j$                  |      dt        j                          v st        j"                  |      rt        j$                  |      ndt        j$                        t        j$                  |      dz  }dd|iz  }|j/                  |       |st        j                  dfdf      t        j$                  |      dt        j                          v st        j"                  |      rt        j$                  |      ndt        j$                        t        j$                  |      dz  }dd|iz  }|j/                  |       t        j0                  |d      i z  }t        j&                  d|       dz   d |iz  } t)        t        j*                  |             dx}x}x}x}	x}
x}x}x}x}x}x}x}x}}y)!uJ   gates 없는 YAML에 대해 cmd_validate는 에러를 출력해야 한다.r   Nr2   zinvalid_no_gates.yaml>)zb%(py7)s
{%(py7)s = %(py0)s(%(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py1)s.strip
}()
})
} > %(py10)slenr   rQ   r   rR   rT   r   r   u,   무효한 YAML인데 아무 출력이 없음z
>assert %(py12)sr   gater   r   r   zD%(py3)s in %(py9)s
{%(py9)s = %(py7)s
{%(py7)s = %(py5)s.lower
}()
}rR   rT   r   r   %(py11)sr   zJ%(py14)s in %(py20)s
{%(py20)s = %(py18)s
{%(py18)s = %(py16)s.lower
}()
}r   py16py18py20%(py22)spy22zJ%(py25)s in %(py31)s
{%(py31)s = %(py29)s
{%(py29)s = %(py27)s.lower
}()
}py25py27py29r   %(py33)sr   r   u"   gates 에러가 출력에 없음: 
>assert %(py36)spy36)r   r   r   r5   r   r   r   rV   r   r   r   stripr  rW   rX   rY   rZ   r[   r\   r]   r^   r_   r   r   r   )!r`   ra   r   r   r   r   r   r   rb   r   @py_assert6r   r   @py_format11r  rc   r   @py_assert13@py_assert17@py_assert19@py_assert15@py_assert24@py_assert28r  @py_assert26r   r   @py_format21@py_format23@py_format32r
  @py_format35r  s!                                    rG   -test_cmd_validate_missing_gates_outputs_errorzBTestAutoOrchValidate.test_cmd_validate_missing_gates_outputs_error$  s   --/G #::  7!34S'($$&,<<V<>Vs>"VQV"Q&VVV"QVVVVVVsVVVsVVVVVV6VVV6VVV<VVV>VVV"VVVQVVV(VVVVVVVV	9	9ll	9"n	9n$	9(/	939<<	93?>	9(/>(A	9EN	9RXR^R^	9R^R`	9ENR`E`	9 	9'8'8	9n	9 	9/8y 	9 	928&	9 	9 8 8 	9 	9/8y 	9 	9/8y #	9 	9/8y %	9 	9 	928&	928	9'8'8	9(/>	9 	9/8y )0	9 	928&	9 	9 8 8 4:	9 	9/8y 4:	9 	9/8y 4@	9 	9/8y 4B	9 	9 	928&	928	9'8'8	9ENR`	9 	9/8y FO	9 	928&	9 	9 8 8 SY	9 	9/8y SY	9 	9/8y S_	9 	9/8y Sa	9 	9 	928&	928	9*8.	9 	9'8'8/x8	9 	9 	9%8%8	9 	9 	9 	9 	9rg   c                    ddl }t        j                  t              }|d= |dz  }|j	                  |j                  |             t        t        |             |j                         }|j                  |j                  z   }g }d}	|j                  }
 |
       }|	|v }|}|s8d}|j                  } |       }||v }|}|sd}|j                  } |       }||v }|}|sxt        j                  d|fd|	|f      t        j                  |	      d	t        j                          v st        j"                  |      rt        j                  |      nd	t        j                  |
      t        j                  |      d
z  }dd|iz  }|j%                  |       |sjt        j                  dfdf      t        j                  |      d	t        j                          v st        j"                  |      rt        j                  |      nd	t        j                        t        j                  |      dz  }dd|iz  }|j%                  |       |st        j                  dfdf      t        j                  |      d	t        j                          v st        j"                  |      rt        j                  |      nd	t        j                        t        j                  |      dz  }dd|iz  }|j%                  |       t        j&                  |d      i z  }t        j(                  d|       dz   d|iz  }t+        t        j,                  |            dx}x}x}	x}x}
x}x}x}x}x}x}x}x}}y)uS   schema_version 없는 YAML에 대해 cmd_validate는 에러를 출력해야 한다.r   Nr.   zinvalid_no_schema.yamlr   r   r   r  r   r  r  r   r  r  r  r  r  r   r$  r   r   u+   schema_version 에러가 출력에 없음: r%  r&  )r   r   r   r5   r   r   r   rV   r   r   r   r   rW   rX   r\   rY   rZ   r[   r   r   r]   r^   r_   )r`   ra   r   r   r   r   r   r   rc   rb   r(  r   r   r   r*  r+  r,  r-  r.  r/  r  r0  r   r   r1  r2  r3  r
  r4  r  s                                 rG   6test_cmd_validate_missing_schema_version_outputs_errorzKTestAutoOrchValidate.test_cmd_validate_missing_schema_version_outputs_error4  s   --/$% #;;  7!34S'($$&,	B	B &	B ,	B.	B29	B=C\\	B=I^	B29^2K	BOX	B\b\h\h	B\h\j	BOX\jOj	B 	B0A0A	B	B 	B8A	 	B 	B;A6	B 	B)A)A !'	B 	B8A	 !'	B 	B8A	 !-	B 	B8A	 !/	B 	B 	B;A6	B;A	B0A0A	B29^	B 	B8A	 3:	B 	B;A6	B 	B)A)A >D	B 	B8A	 >D	B 	B8A	 >J	B 	B8A	 >L	B 	B 	B;A6	B;A	B0A0A	BOX\j	B 	B8A	 PY	B 	B;A6	B 	B)A)A ]c	B 	B8A	 ]c	B 	B8A	 ]i	B 	B8A	 ]k	B 	B 	B;A6	B;A	B3A>	B 	B0A0A8A	B 	B 	B.A.A	B 	B 	B 	B 	Brg   N)
r   r   r   r   r   r   r   r  r5  r7  r   rg   rG   r   r      s&    ;5X)V9 Brg   r   c                   (    e Zd ZdZd Zd Zd Zd Zy)TestAutoOrchStatusListu*   cmd_status + cmd_list 테스트 스위트.c                    |dz  }|j                          ddddd}|dz  j                  t        j                  |             |j	                  dt        |             t                |j                         }|j                  |j                  z   }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}}	g }d}	|j&                  } |       }|	|v }|}|sd}|j&                  } |       }||v }|}|st        j                  d	|fd|	|f      t        j                  |	      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }|j)                  |       |st        j                  d	fdf      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                        t        j                  |      dz  }dd|iz  }|j)                  |       t        j*                  |d      i z  }t        j                   d|       dz   d|iz  }t#        t        j$                  |            dx}x}x}	x}x}x}x}x}x}}y)u]   state/*.json이 있을 때 cmd_status는 파이프라인 ID와 상태를 출력해야 한다.stater   runningr%   z2026-03-24T10:00:00)pipeline_idstatuscurrent_step
started_atztest-pipeline-001.json orchestrator.auto_orch.STATE_DIRr   z%(py1)s in %(py3)sr   r   rR   u/   파이프라인 ID가 status 출력에 없음: rS   rT   Nstepr  r  r  r   r  r  r  r  r   u*   상태 정보가 status 출력에 없음: z
>assert %(py25)sr!  )mkdirr   jsondumpsr   rV   r   r   r   r   rW   rX   r\   rY   rZ   r[   r]   r^   r_   r   r   r   )r`   ra   r   r   	state_dir
state_datar   r   r   rb   rd   re   rc   r(  r   r   r*  r+  r,  r-  r   r   r1  r2  @py_format24@py_format26s                             rG   %test_cmd_status_with_running_pipelinez<TestAutoOrchStatusList.test_cmd_status_with_running_pipelineL  s   w&	.$/	

 
-	-99$**Z:PQ>IO$$&,"h"f,hhh"fhhh"hhhhhhfhhhfhhhh0_`f_g.hhhhhhh}y}FLL}LN}yN*}f}}}f.F}}}}yN}}}y}}}}}}F}}}F}}}L}}}N}}}}}}}f}}}f}}}}}}}}}}}}}}}}}}}}}}}}}Jtu{t|H}}}}}}}}}rg   c           	         |dz  }|j                          |j                  dt        |             t                |j	                         }|j
                  |j                  z   j                         }|j                  } |       }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                  |      t        j                  |	      t        j                  |
      dz  }d	d
|iz  }t!        t        j"                  |            dx}x}x}	x}}
y)ub   state/가 비어있을 때 cmd_status는 실행 중 파이프라인 없음을 출력해야 한다.r;  rA  r   )>=)zc%(py7)s
{%(py7)s = %(py0)s(%(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py1)s.strip
}()
})
} >= %(py10)sr  r   r  zassert %(py12)sr   N)rE  r   rV   r   r   r   r   r   r'  r  rW   rX   rY   rZ   r[   r\   r^   r_   )r`   ra   r   r   rH  r   r   rb   r   r(  r   r   r)  r  s                 rG   test_cmd_status_empty_state_dirz6TestAutoOrchStatusList.test_cmd_status_empty_state_dir^  s   w&	>IO$$&,,-446<<'<>'s>"'a'"a''''"a''''''s'''s''''''6'''6'''<'''>'''"'''a''''''''rg   c                    ddl }|dz  }|j                          t        j                  t              }|dz  j                  |j                  |             |j                  dt        |             t                |j                         }|j                  |j                  z   }g }	d}
|
|v }|}|sd}|j                  } |       }||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  }|	j)                  |       |st        j                  dfdf      t        j                   |      d
t#        j$                         v st        j&                  |      rt        j                   |      nd
t        j                         t        j                   |      dz  }dd|iz  }|	j)                  |       t        j*                  |	d      i z  }t        j,                  d|       dz   d|iz  }t/        t        j0                  |            dx}x}	x}
x}x}x}x}}y)uY   pipelines/*.yaml이 있을 때 cmd_list는 파이프라인 목록을 출력해야 한다.r   N	pipelinesztest-pipeline-001.yaml$orchestrator.auto_orch.PIPELINES_DIRr   ztest-pipeliner   )z%(py3)s in %(py5)sr   r   r   r   )zJ%(py10)s in %(py16)s
{%(py16)s = %(py14)s
{%(py14)s = %(py12)s.lower
}()
})r   r   r   r  z%(py18)sr  r   u1   파이프라인 목록이 list 출력에 없음: z
>assert %(py21)sr   )r   rE  r   r   r5   r   r   r   rV   r   r   r   r   r   rW   rX   r\   rY   rZ   r[   r   r   r]   r^   r_   )r`   ra   r   r   r   pipelines_dirpipeline_datar   r   rc   rb   r   r   r   r*  r-  r   re   r  @py_format17@py_format19r  r  s                          rG   test_cmd_list_with_pipelinesz3TestAutoOrchStatusList.test_cmd_list_with_pipelinesi  sn    ;.n5	1	1==dii>VWBCDVW
$$&,	H	H6)	H-<	H@F	H@L	H-<-N	H 	H6G6G	H6	H 	H>Gi  	H 	HAG	H 	H/G/G $*	H 	H>Gi $*	H 	H 	HAG	HAG	H6G6G	H-<	H 	H>Gi .=	H 	HAG	H 	H/G/G AG	H 	H>Gi AG	H 	H>Gi AM	H 	H>Gi AO	H 	H 	HAG	HAG	H9G	H 	H6G6G>vhG	H 	H 	H4G4G	H 	H 	H 	Hrg   c                     |dz  }|j                          |j                  dt        |             t                |j	                         }y)uP   pipelines/가 비어있을 때 cmd_list는 에러 없이 실행되어야 한다.rQ  rR  N)rE  r   rV   r   r   )r`   ra   r   r   rS  r   s         rG   !test_cmd_list_empty_pipelines_dirz8TestAutoOrchStatusList.test_cmd_list_empty_pipelines_diry  sA     ;.BCDVW
$$&rg   N)r   r   r   r   rL  rO  rW  rY  r   rg   rG   r9  r9  I  s    4~$	(H 'rg   r9  c                   4    e Zd ZdZd Zd Zd Zd Zd Zd Z	y)	TestAutoOrchScanu;   cmd_scan + scan_events + dispatch_step 테스트 스위트.c                 z   |dz  }|dz  }|j                          |j                          |dz  j                  d       |dz  j                  d       |dz  j                  d       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}}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)u_   incoming/에 .done 파일이 있을 때 scan_events는 해당 파일명을 반환해야 한다.incoming	processedr"   donezpipeline-002.doneznot-a-done-file.txtignorer   rB  r   rC  u,   pipeline-001.done이 scan 결과에 없음: rS   rT   Nu,   pipeline-002.done이 scan 결과에 없음: r   z%(py1)s not in %(py3)su2   .done이 아닌 파일이 scan 결과에 포함됨rE  r   r   rV   rW   rX   r\   rY   rZ   r[   r]   r^   r_   	r`   ra   r]  r^  r   r   rb   rd   re   s	            rG   !test_scan_events_finds_done_filesz2TestAutoOrchScan.test_scan_events_finds_done_files  s   j({*		'	'33F;	'	'33F;	)	)55h?c(mS^<"g"g-ggg"gggg"ggggggggggggggg1]^e]f/ggggggg"g"g-ggg"gggg"ggggggggggggggg1]^e]f/ggggggg$i$G3iii$Giii$iiiiiiGiiiGiiii5iiiiiiirg   c                    |dz  }|dz  }|j                          |j                          |dz  j                  d       |dz  j                  d       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)u\   processed/에 이미 있는 .done 파일은 scan_events 결과에서 제외되어야 한다.r]  r^  zpipeline-new.doner_  zpipeline-old.doner   rB  r   rC  u'   신규 .done 파일이 결과에 없음rS   rT   Nr   ra  u4   이미 처리된 .done 파일이 결과에 포함됨rb  rc  s	            rG   +test_scan_events_excludes_already_processedz<TestAutoOrchScan.test_scan_events_excludes_already_processed  s6   j({*		'	'33F;	(	(44V<c(mS^<"X"g-XXX"gXXX"XXXXXXgXXXgXXXX/XXXXXXX"i"'1iii"'iii"iiiiii'iii'iiii3iiiiiiirg   c                    dddddd}dddd	}t        d
      5 }t        d      |_        t        |d|       |j                  }|st        j                  d      dz   dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }t        t        j                  |            d}ddd       y# 1 sw Y   yxY w)uG   dispatch_step은 내부적으로 subprocess.run을 호출해야 한다.r%   z	Test Stepr   r&   r'   r(   r   r<  r=  r>  r?  %orchestrator.auto_orch.subprocess.runr   
returncodeu6   dispatch_step이 subprocess.run을 호출하지 않음z,
>assert %(py2)s
{%(py2)s = %(py0)s.called
}mock_run)rQ   ro   N)r   r   return_valuer   calledrW   r]   rY   rZ   r[   r\   r^   r_   )r`   ra   rD  r;  rl  rc   @py_format3s          rG   'test_dispatch_step_calls_subprocess_runz8TestAutoOrchScan.test_dispatch_step_calls_subprocess_run  s     &"?!
 /$

 :; 	]x$-$;H!$ 3U;??\?\\$\\\\\\\8\\\8\\\?\\\\\\	] 	] 	]s   B>C!!C*c                    |dz  }|dz  }|j                          |j                          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	)
u2   scan_events는 항상 list를 반환해야 한다.r]  r^  u-   scan_events 반환 타입이 list가 아님: r   r   ry   listr   N)rE  r   rV   r   rr  rW   r]   r   rY   rZ   r[   r\   r^   r_   )r`   ra   r]  r^  ry   rr   rs   s          rG   test_scan_events_returns_listz.TestAutoOrchScan.test_scan_events_returns_list  s    j({*	S]C	N;&$'g'gg+XY]^dYeXf)gggggggzgggzgggggg&ggg&gggggg$ggg$ggg'ggggggrg   c                 B   |dz  }|j                          |j                  dt        |             dddd}t        d|       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}}|j                  }d}
 ||
      }d}||k(  }|st        j                  d|fd||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |
      t        j                  |      t        j                  |      dz  }t        j                  d|       dz   d|iz  }t        t        j                  |            dx}x}
x}x}}y)u]   save_pipeline_state + get_pipeline_state 라운드트립이 올바르게 동작해야 한다.r;  rA  r   r<  r%   rh  NrL   rN   loadedrP   u2   저장 후 get_pipeline_state가 None을 반환함rS   rT   r>  r   r   r   u)   로드된 상태가 올바르지 않음: 
>assert %(py11)sr   )rE  r   rV   r   r   rW   rX   rY   rZ   r[   r\   r]   r^   r_   r   )r`   ra   r   rH  r;  ru  rb   rc   rd   re   rr   r   r   r   r   r   s                   rG   *test_save_and_get_pipeline_state_roundtripz;TestAutoOrchScan.test_save_and_get_pipeline_state_roundtrip  sa   w&	>IO /$

 	/7#$78!WvT!WWWvTWWWWWWvWWWvWWWTWWW#WWWWWWWzzf(fz(#fyf#y0fff#yffffffvfffvfffzfff(fff#fffyfff4]^d]e2ffffffffrg   c                 J   |dz  }|j                  dt        |             t        dd       |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        j                  |j                               }|j                  }d} ||      }d}	||	k(  }
|
st	        j                   d|
fd||	f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      t	        j                  |      t	        j                  |	      dz  }t	        j
                  d|       dz   d|iz  }t        t	        j                  |            d
x}x}x}x}
}	y
)u;   update_health는 health.json을 생성/갱신해야 한다.zhealth.jsonz"orchestrator.auto_orch.HEALTH_PATHr}   r   )active_pipelineserrorsu4   update_health 후 health.json이 생성되지 않음rm   health_pathrn   Nry  r   r   datar   u-   active_pipelines 값이 올바르지 않음: rv  r   )r   rV   r   rq   rW   r]   rY   rZ   r[   r\   r^   r_   rF  loads	read_textr   rX   )r`   ra   r   r{  rc   rr   rs   r|  r   r   r   r   r   s                rG   &test_update_health_creates_health_jsonz7TestAutoOrchScan.test_update_health_creates_health_json  sZ   .@#kBRSq3!![!#[#[[%[[[[[[[{[[[{[[[![[[#[[[[[[zz+//12xxh*hx*+hqh+q0hhh+qhhhhhhthhhthhhxhhh*hhh+hhhqhhh4abfag2hhhhhhhhrg   N)
r   r   r   r   rd  rf  rp  rs  rw  r  r   rg   rG   r[  r[    s)    Ejj]&hg irg   r[  c                   (    e Zd ZdZd Zd Zd Zd Zy)TestCrashRecoveryu7   비정상 state JSON 파일 처리 테스트 스위트.c                    |dz  }|j                          |j                  dt        |             |dz  j                  d       t	        d      }g }d}||u }|}|st        |t              }	|	}|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  }|j                  |       |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	dt        j                         v st        j                  t              rt        j                  t              ndt        j                  	      dz  }|j                  |       t        j                  |d      i z  }t        j                  d|       dz   d|iz  }t!        t        j"                  |            dx}x}x}x}}	y)uj   비정상 JSON 파일이 있으면 get_pipeline_state는 None 또는 초기 상태를 반환해야 한다.r;  rA  zcrashed-pipeline.jsonz{invalid json content !!!zcrashed-pipelineNrv   z%(py2)s is %(py5)sry   ro   rT   r   r   2%(py13)s
{%(py13)s = %(py9)s(%(py10)s, %(py11)s)
}r   r   r   r   r   py13r   u2   비정상 JSON 처리 결과가 예상치 않음: 
>assert %(py16)sr  rE  r   rV   r   r   r   r   rW   rX   rY   rZ   r[   r\   r   r   r]   r^   r_   r`   ra   r   rH  ry   rc   r   rr   r   @py_assert12re   r  @py_format14r  rU  s                  rG   8test_get_pipeline_state_with_corrupted_json_returns_nonezJTestCrashRecovery.test_get_pipeline_state_with_corrupted_json_returns_none  sS   w&	>IO	,	,889TU#$67xxv~xFD!9x!9xxxvxxxxxxvxxxvxxxxxxxxxxxxxxxxxxxxxxFxxxFxxxxxxDxxxDxxx!9xxxxxxxxx=opvow;xxxxxxxxrg   c                    |dz  }|j                          |j                  dt        |             |dz  j                  d       t	        d      }g }d}||u }|}|st        |t              }	|	}|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  }|j                  |       |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	dt        j                         v st        j                  t              rt        j                  t              ndt        j                  	      dz  }|j                  |       t        j                  |d      i z  }t        j                  d|       dz   d|iz  }t!        t        j"                  |            dx}x}x}x}}	y)uT   빈 JSON 파일이 있으면 get_pipeline_state는 안전하게 처리해야 한다.r;  rA  zempty-pipeline.json zempty-pipelineNrv   r  ry   r  r   r   r  r   r   r  r   u.   빈 파일 처리 결과가 예상치 않음: r  r  r  r  s                  rG   4test_get_pipeline_state_with_empty_json_returns_nonezFTestCrashRecovery.test_get_pipeline_state_with_empty_json_returns_none  sR   w&	>IO	*	*66r:#$45ttv~tFD!9t!9tttvttttttvtttvttttttttttttttttttttttFtttFttttttDtttDttt!9ttttttttt=klrks;ttttttttrg   c                    |dz  }|j                          |j                  d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
      dz   d|iz  }t        t	        j                  |            dx}}y)uc   존재하지 않는 파이프라인 ID에 대해 get_pipeline_state는 None을 반환해야 한다.r;  rA  znonexistent-pipeline-xyzNrv   rx   ry   rP   u2   존재하지 않는 pipeline인데 None이 아닌 r   rS   rT   )rE  r   rV   r   rW   rX   rY   rZ   r[   r\   r]   r^   r_   )	r`   ra   r   rH  ry   rb   rc   rd   re   s	            rG   0test_get_pipeline_state_nonexistent_returns_nonezBTestCrashRecovery.test_get_pipeline_state_nonexistent_returns_none  s    w&	>IO#$>?iv~iiiviiiiiiviiiviiiiii!STZS[[hiiiiiiirg   c                 x   |dz  }|j                          |j                  dt        |             dddd}|dz  j                  t	        j
                  |             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}}|j                   }d}
 ||
      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |
      t        j                  |      t        j                  |      dz  }t        j                  d|       dz   d|iz  }t        t        j                  |            dx}x}
x}x}}y)uM   정상 JSON 파일은 get_pipeline_state가 올바르게 로드해야 한다.r;  rA  zrecover-test	completedr}   )r=  r>  
steps_donezrecover-test.jsonNrL   rN   ry   rP   u   정상 JSON인데 None 반환rS   rT   r>  r   r   r   u#   status 값이 올바르지 않음: rv  r   )rE  r   rV   r   rF  rG  r   rW   rX   rY   rZ   r[   r\   r]   r^   r_   r   )r`   ra   r   rH  rI  ry   rb   rc   rd   re   rr   r   r   r   r   r   s                   rG   2test_get_pipeline_state_valid_json_loads_correctlyzDTestCrashRecovery.test_get_pipeline_state_valid_json_loads_correctly  sk   w&	>IO%3{Z[\
	(	(44TZZ
5KL#N3!BvT!BBBvTBBBBBBvBBBvBBBTBBB#BBBBBBBzzb(bz(#b{b#{2bbb#{bbbbbbvbbbvbbbzbbb(bbb#bbb{bbb6YZ`Ya4bbbbbbbbrg   N)r   r   r   r   r  r  r  r  r   rg   rG   r  r    s    A	yuj	crg   r  c                       e Zd ZdZd Zd Zy)TestDispatchShellFalseu3   dispatch_step의 subprocess.run shell=False 검증.c                    |j                  dt        |dz               |dz  j                          dddddd}d	d
dd}g fd}t        d|      5  t	        |d	|       ddd       t              }d}||kD  }|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}}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  }t        j                  d| d       d!z   d"|iz  }t        t        j                  |            dx}} y# 1 sw Y   
xY w)#uF   dispatch_step은 subprocess.run을 shell=False로 호출해야 한다.rA  r;  r%   zSecurity Stepr   r&   r'   r(   zsecurity-pipeliner<  rh  c                  >    j                  |       t        d      S )Nr   rj  r   r   )r6   kwargscaptured_kwargss     rG   rl  zLTestDispatchShellFalse.test_dispatch_step_uses_shell_false.<locals>.mock_run(  s    ""6***rg   ri  side_effectNr   r  )z/%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} > %(py6)sr  r  )rQ   r   rR   r   u%   subprocess.run이 호출되지 않음z
>assert %(py8)spy8shellFrv   rx   	shell_valrP   u   shell=False가 아닌 shell=u   로 subprocess.run이 호출됨rS   rT   )r   rV   rE  r   r   r  rW   rX   rY   rZ   r[   r\   r]   r^   r_   r   )r`   ra   r   rD  r;  rl  rb   r   r   @py_format7@py_format9r  r  rc   rd   re   r  s                   @rG   #test_dispatch_step_uses_shell_falsez:TestDispatchShellFalse.test_dispatch_step_uses_shell_false  s   >HwDV@WX	G	""$ #&"?!
 /$

 ')	+ :Q 	<$ 3U;	< ?#PaP#a'PPP#aPPPPPPsPPPsPPPPPP?PPP?PPP#PPPaPPP)PPPPPPPP% 	qF

7E2I %p9%ppp9pppppp9ppp9pppppp)Ei[Po'ppppppp	q		< 	<s   I++I5c                   	 |j                  dt        |dz               |dz  j                          dddddd}d	d
d}g 		fd}t        d|      5  t	        |d	|       ddd       	rI	d   }t        |t              }|s.t        j                  dt        |       d      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y# 1 sw Y   WxY w)um   dispatch_step은 subprocess.run에 문자열이 아닌 리스트를 첫 번째 인자로 전달해야 한다.rA  r;  r%   zList Args Stepr   r&   r'   r(   zlist-arg-pipeliner<  )r=  r>  c                  z    | rj                  | d          nd|v rj                  |d          t        d      S )Nr   r6   rj  r  )r6   r  captured_argss     rG   rl  z`TestDispatchShellFalse.test_dispatch_step_passes_list_not_string_to_subprocess.<locals>.mock_runC  s=    $$T!W-6!$$VF^4**rg   ri  r  Nr   u6   subprocess.run의 첫 번째 인자가 list가 아닌 u   임 (shell injection 위험)r   r   	first_argrr  r   )r   rV   rE  r   r   r   rr  rW   r]   r   rY   rZ   r[   r\   r^   r_   )
r`   ra   r   rD  r;  rl  r  rr   rs   r  s
            @rG   7test_dispatch_step_passes_list_not_string_to_subprocesszNTestDispatchShellFalse.test_dispatch_step_passes_list_not_string_to_subprocess4  s   >HwDV@WX	G	""$ $&"?!
 !4yI 	+ :Q 	<$ 3U;	< %a(I4 v  vduduGYGXXtuv vououv v]u]u  v vlulu  v vououv v]u]u v vlulu v vououv v]u]u  v vlulu  v vlulu v v vbubuv v 	< 	<s   F77GN)r   r   r   r   r  r  r   rg   rG   r  r    s    =q>vrg   r  )5r   builtinsrY   _pytest.assertion.rewrite	assertionrewriterW   r   rF  r   os
subprocessr;   tempfilepathlibr   unittest.mockr   r   r   r   r<   r=   r>   orchestrator.auto_orchr   r   r	   r
   r   r   r   r   r   r   r   r   r   r?   r   r5   r   __annotations__tupleboolrH   rJ   r   r   r9  r[  r  r  r   rg   rG   <module>r     s]       	  
   0 0 
 '!HHOOA~&
    , 
#!;/ )		
  *=> &"?!	
 &"@#*!	
!! !Re  *J Jd() ()`LB LBh6' 6'~Ri Rit(c (c`?v ?vrg   