
    Qi'j                        U d 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*      Z* G d+ d,      Z+ G d- d.      Z, G d/ d0      Z- G d1 d2      Z.y)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           O/home/jay/workspace/.worktrees/task-2117-dev1/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                 B    t        dt        |            }|J d       y)uR   TeamLock(team_id, locks_dir)으로 인스턴스를 생성할 수 있어야 한다.r   r9   Nu#   TeamLock 인스턴스 생성 실패r   str)selftmp_pathlocks      rG   test_teamlock_instantiationz(TestTeamLock.test_teamlock_instantiationv   s$    s8}=F!FF    c                     t        dt        |            5 }|J d       	 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   Nu   __enter__ 반환값이 None임zdev2-team.lockrL   )rN   rO   tl	lock_files       rG   3test_teamlock_context_manager_acquires_and_releasesz@TestTeamLock.test_teamlock_context_manager_acquires_and_releases{   ss    kS]; 	Dr>C#CC>	D //	kS]; 		 		D 	D
	 	s   
AAAA%c                     d}t        |t        |            5  || dz  }|j                         sJ d| d       	 ddd       y# 1 sw Y   yxY w)uQ   TeamLock 획득 시 locks_dir에 <team_id>.lock 파일이 생성되어야 한다.r   r9   z.locku   락 파일 u   이 생성되지 않음N)r   rM   exists)rN   rO   rC   rU   s       rG   test_teamlock_creates_lock_filez,TestTeamLock.test_teamlock_creates_lock_file   s`    gX7 	X gYe#44I##%WYK?V'WW%	X 	X 	Xs   $AAc                 Z    t        j                  dt        |            }|du sJ d       y)uE   락이 없을 때 is_team_available()은 True를 반환해야 한다.z	free-teamr9   Tu?   락이 없는 팀인데 is_team_available이 False를 반환함N)r   is_team_availablerM   )rN   rO   results      rG   1test_is_team_available_returns_true_when_unlockedz>TestTeamLock.test_is_team_available_returns_true_when_unlocked   s*    ++K3x=Q~```~rR   c                 X   d}t        j                  d      }d }t        j                  |t        |      ||f      }|j	                          |j                          t        j                  |t        |            }|j                          |j                  d       |du sJ d	       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      )timeoutFuB   락 보유 중인 팀인데 is_team_available이 True를 반환함N)	multiprocessingBarrierProcessrM   startra   r   r[   join)rN   rO   rC   rc   rd   pr\   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d ddrR   c                 *   d}t        |      |df}t        j                  d      5 }|j                  t        ||g      }ddd       j                  d      }|j                  d      }|dk(  s
J d|        |dk(  s
J d	|        y# 1 sw Y   JxY w)
uh   2개 프로세스가 동시에 동일 팀 락 획득 시도 → 1개만 성공, 1개는 BlockingIOError.zcontested-teamNr_   )	processesTF   u9   정확히 1개 프로세스만 True여야 하는데 True=u;   정확히 1개 프로세스만 False여야 하는데 False=)rM   rh   PoolmaprH   count)rN   rO   rC   r6   poolresults
true_countfalse_counts           rG   )test_two_processes_only_one_acquires_lockz6TestTeamLock.test_two_processes_only_one_acquires_lock   s    " Hw-!!A. 	B$hh2T4LAG	B ]]4(
mmE*Qh"[\f[g hhal#^_j^k!ll	B 	Bs   B		B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   NrL   )rN   rO   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__rQ   rV   rY   r]   rn   ry   r{    rR   rG   rJ   rJ   s   s-    1G
Xa
e4mrR   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               }	 |J d       t        |t              sJ dt        |       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_PATHN5   첫 번째 acquire_global_lock()이 None을 반환함u'   acquire_global_lock()이 int가 아닌    를 반환함)rM   setattrr   
isinstanceintr   r   )rN   rO   monkeypatch	lock_pathfds        rG   #test_acquire_global_lock_returns_fdz2TestGlobalLock.test_acquire_global_lock_returns_fd   s    #889	EyQ "	(>Z#ZZ>b#&i*QRVWYRZQ[[h(ii&~#B' r~#B' s   -A) )A9c                     t        |dz        }|j                  d|       t               }	 |J d       t               }|J d| d       	 |t        |       yy# |t        |       w w xY w)u\   acquire_global_lock() 두 번째 호출 → None을 반환해야 한다 (이미 락 보유).ztest-auto-orch2.lockr   Nr   u3   두 번째 acquire_global_lock()이 None이 아닌 r   rM   r   r   r   rN   rO   r   r   fd1fd2s         rG   1test_acquire_global_lock_second_call_returns_nonez@TestGlobalLock.test_acquire_global_lock_second_call_returns_none   s    #99:	EyQ!#	)?[$[[?%'C;h"UVYUZZg hh;#C( s#C( s    A A,c                     t        |dz        }|j                  d|       t               }|J t        |       t               }	 |J d       	 |t        |       yy# |t        |       w w xY w)uX   release_global_lock() 후 동일 경로의 락을 다시 획득할 수 있어야 한다.ztest-auto-orch3.lockr   Nu-   락 해제 후 재획득이 None을 반환함r   r   s         rG   )test_release_global_lock_allows_reacquirez8TestGlobalLock.test_release_global_lock_allows_reacquire   s    #99:	EyQ!#C !#	)?S$SS?#C( s#C( s   	A A.N)r|   r}   r~   r   r   r   r   r   rR   rG   r   r      s    H
())rR   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J d       |j                  d      dk(  sJ y)uV   유효한 YAML 파일을 load_pipeline으로 로드하면 dict를 반환해야 한다.r   Nzvalid_pipeline.yamlu1   유효한 YAML인데 dict가 아닌 타입 반환r.   r   )	yaml
write_textdumpr5   r   rM   r   dictget)rN   rO   r   pipeline_filer\   s        rG   test_load_pipeline_valid_yamlz2TestAutoOrchValidate.test_load_pipeline_valid_yaml   sd     #88  >!:;s=12&$'\)\\'zz*+u444rR   c                     ddl }t        j                  t              }|d= |dz  }|j	                  |j                  |             t        t        |            }t        |t              sJ d       y)uj   schema_version 없는 YAML은 load_pipeline이 에러를 보고하거나 예외를 발생시켜야 한다.r   Nr.   zinvalid_pipeline.yamlu,   load_pipeline이 dict를 반환하지 않음)
r   copydeepcopyr5   r   r   r   rM   r   r   )rN   rO   r   invalidr   r\   s         rG   :test_load_pipeline_missing_schema_version_raises_or_errorszOTestAutoOrchValidate.test_load_pipeline_missing_schema_version_raises_or_errors  se    --/$% #::  7!34 s=12&$'W)WW'rR   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   rM   )rN   rO   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dl }|dz  }|j                  |j                  t                     t	        t        |             |j                         }|j                  |j                  z   j                         }d|vs4d|v s/d|v s*d|v s%d|v s J d	|j                   |j                          yyyyy)
uN   유효한 YAML에 대해 cmd_validate는 에러 없음을 출력해야 한다.r   Nz
valid.yamlerrorz0 errorvalidokpassu+   유효한 YAML인데 에러 출력 발생: )
r   r   r   r5   r   rM   
readouterrouterrlower)rN   rO   capsysr   r   capturedoutputs          rG   -test_cmd_validate_valid_yaml_outputs_no_errorzBTestAutoOrchValidate.test_cmd_validate_valid_yaml_outputs_no_error  s     </  >!:;S'($$&,,-446 6!Y&%8Gv<MQUY_Q_cimscs	V8x||nU	VscsQ_<M%8!rR   c                    ddl }t        j                  t              }|d= |dz  }|j	                  |j                  |             t        t        |             |j                         }|j                  |j                  z   }t        |j                               dkD  sJ d       d|j                         v s0d|j                         v sd|j                         v s
J d	|        yyy)
uJ   gates 없는 YAML에 대해 cmd_validate는 에러를 출력해야 한다.r   Nr2   zinvalid_no_gates.yamlu,   무효한 YAML인데 아무 출력이 없음gater   r   u"   gates 에러가 출력에 없음: )r   r   r   r5   r   r   r   rM   r   r   r   lenstripr   rN   rO   r   r   r   r   r   r   s           rG   -test_cmd_validate_missing_gates_outputs_errorzBTestAutoOrchValidate.test_cmd_validate_missing_gates_outputs_error$  s    --/G #::  7!34S'($$&,6<<>"Q&V(VV&flln$6<<>(AYRXR^R^R`E`	9/x8	9`E`(A$rR   c                    ddl }t        j                  t              }|d= |dz  }|j	                  |j                  |             t        t        |             |j                         }|j                  |j                  z   }d|j                         v s0d|j                         v sd|j                         v s
J d|        yyy)uS   schema_version 없는 YAML에 대해 cmd_validate는 에러를 출력해야 한다.r   Nr.   zinvalid_no_schema.yamlr   r   u+   schema_version 에러가 출력에 없음: )r   r   r   r5   r   r   r   rM   r   r   r   r   r   s           rG   6test_cmd_validate_missing_schema_version_outputs_errorzKTestAutoOrchValidate.test_cmd_validate_missing_schema_version_outputs_error4  s    --/$% #;;  7!34S'($$&,.'V\\^2Ky\b\h\h\jOj	B8A	BjOj2K.rR   N)
r|   r}   r~   r   r   r   r   r   r   r   r   rR   rG   r   r      s&    ;5X)V9 BrR   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 s
J d	|        d|j                         v sd
|j                         v s
J d|        y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_DIRu/   파이프라인 ID가 status 출력에 없음: stepu*   상태 정보가 status 출력에 없음: N)mkdirr   jsondumpsr   rM   r   r   r   r   r   )rN   rO   r   r   	state_dir
state_datar   r   s           rG   %test_cmd_status_with_running_pipelinez<TestAutoOrchStatusList.test_cmd_status_with_running_pipelineL  s    w&	.$/	

 
-	-99$**Z:PQ>IO$$&,"f,h0_`f_g.hh,FLLN*f.F}Jtu{t|H}}F.F*rR   c                 "   |dz  }|j                          |j                  dt        |             t                |j	                         }|j
                  |j                  z   j                         }t        |j                               dk\  sJ y)ub   state/가 비어있을 때 cmd_status는 실행 중 파이프라인 없음을 출력해야 한다.r   r   r   N)
r   r   rM   r   r   r   r   r   r   r   )rN   rO   r   r   r   r   r   s          rG   test_cmd_status_empty_state_dirz6TestAutoOrchStatusList.test_cmd_status_empty_state_dir^  sr    w&	>IO$$&,,-4466<<>"a'''rR   c                    ddl }|dz  }|j                          t        j                  t              }|dz  j                  |j                  |             |j                  dt        |             t                |j                         }|j                  |j                  z   }d|v sd|j                         v s
J d|        yy)	uY   pipelines/*.yaml이 있을 때 cmd_list는 파이프라인 목록을 출력해야 한다.r   N	pipelinesztest-pipeline-001.yaml$orchestrator.auto_orch.PIPELINES_DIRr   ztest-pipelineu1   파이프라인 목록이 list 출력에 없음: )r   r   r   r   r5   r   r   r   rM   r   r   r   r   r   )	rN   rO   r   r   r   pipelines_dirpipeline_datar   r   s	            rG   test_cmd_list_with_pipelinesz3TestAutoOrchStatusList.test_cmd_list_with_pipelinesi  s     ;.n5	1	1==dii>VWBCDVW
$$&,6)_-N	H>vhG	HN-N)rR   c                     |dz  }|j                          |j                  dt        |             t                |j	                         }y)uP   pipelines/가 비어있을 때 cmd_list는 에러 없이 실행되어야 한다.r   r   N)r   r   rM   r   r   )rN   rO   r   r   r   r   s         rG   !test_cmd_list_empty_pipelines_dirz8TestAutoOrchStatusList.test_cmd_list_empty_pipelines_diry  sA     ;.BCDVW
$$&rR   N)r|   r}   r~   r   r   r   r   r   r   rR   rG   r   r   I  s    4~$	(H 'rR   r   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 s
J d|        d|v s
J d	|        d|vsJ d
       y)u_   incoming/에 .done 파일이 있을 때 scan_events는 해당 파일명을 반환해야 한다.incoming	processedr"   donezpipeline-002.doneznot-a-done-file.txtignoreu,   pipeline-001.done이 scan 결과에 없음: u,   pipeline-002.done이 scan 결과에 없음: u2   .done이 아닌 파일이 scan 결과에 포함됨Nr   r   r   rM   rN   rO   r   r   rv   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-g1]^e]f/gg-"g-g1]^e]f/gg-$G3i5ii3rR   c                    |dz  }|dz  }|j                          |j                          |dz  j                  d       |dz  j                  d       t        t        |      t        |            }d|v sJ d       d|vsJ d       y)	u\   processed/에 이미 있는 .done 파일은 scan_events 결과에서 제외되어야 한다.r   r   zpipeline-new.doner   zpipeline-old.doneu'   신규 .done 파일이 결과에 없음u4   이미 처리된 .done 파일이 결과에 포함됨Nr   r   s        rG   +test_scan_events_excludes_already_processedz<TestAutoOrchScan.test_scan_events_excludes_already_processed  s    j({*		'	'33F;	(	(44V<c(mS^<"g-X/XX-"'1i3ii1rR   c                     dddddd}dddd	}t        d
      5 }t        d      |_        t        |d|       |j                  s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을 호출하지 않음N)r   r   return_valuer   called)rN   rO   r   r   mock_runs        rG   'test_dispatch_step_calls_subprocess_runz8TestAutoOrchScan.test_dispatch_step_calls_subprocess_run  s~     &"?!
 /$

 :; 	]x$-$;H!$ 3U;??\$\\?	] 	] 	]s   2AAc                     |dz  }|dz  }|j                          |j                          t        t        |      t        |            }t        |t              sJ dt        |              y)u2   scan_events는 항상 list를 반환해야 한다.r   r   u-   scan_events 반환 타입이 list가 아님: N)r   r   rM   r   listr   )rN   rO   r   r   r\   s        rG   test_scan_events_returns_listz.TestAutoOrchScan.test_scan_events_returns_list  sc    j({*	S]C	N;&$'g+XY]^dYeXf)gg'rR   c                     |dz  }|j                          |j                  dt        |             dddd}t        d|       t	        d      }|J d       |j                  d	      dk(  s
J d
|        y)u]   save_pipeline_state + get_pipeline_state 라운드트립이 올바르게 동작해야 한다.r   r   r   r   r%   r   Nu2   저장 후 get_pipeline_state가 None을 반환함r   u)   로드된 상태가 올바르지 않음: )r   r   rM   r   r   r   )rN   rO   r   r   r   loadeds         rG   *test_save_and_get_pipeline_state_roundtripz;TestAutoOrchScan.test_save_and_get_pipeline_state_roundtrip  s    w&	>IO /$

 	/7#$78!W#WW!zz(#y0f4]^d]e2ff0rR   c                    |dz  }|j                  dt        |             t        dd       |j                         sJ d       t	        j
                  |j                               }|j                  d      dk(  s
J d|        y	)
u;   update_health는 health.json을 생성/갱신해야 한다.zhealth.jsonz"orchestrator.auto_orch.HEALTH_PATHr_   r   )active_pipelineserrorsu4   update_health 후 health.json이 생성되지 않음r   u-   active_pipelines 값이 올바르지 않음: N)r   rM   r   rX   r   loads	read_textr   )rN   rO   r   health_pathdatas        rG   &test_update_health_creates_health_jsonz7TestAutoOrchScan.test_update_health_creates_health_json  s    .@#kBRSq3!!#[%[[#zz+//12xx*+q0h4abfag2hh0rR   N)
r|   r}   r~   r   r   r   r   r   r   r   r   rR   rG   r   r     s)    Ejj]&hg irR   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      }|t        |t              s
J d|        yy)uj   비정상 JSON 파일이 있으면 get_pipeline_state는 None 또는 초기 상태를 반환해야 한다.r   r   zcrashed-pipeline.jsonz{invalid json content !!!zcrashed-pipelineNu2   비정상 JSON 처리 결과가 예상치 않음: r   r   rM   r   r   r   r   rN   rO   r   r   r\   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~FD!9x=opvow;xx9!9~rR   c                     |dz  }|j                          |j                  dt        |             |dz  j                  d       t	        d      }|t        |t              s
J d|        yy)uT   빈 JSON 파일이 있으면 get_pipeline_state는 안전하게 처리해야 한다.r   r   zempty-pipeline.json zempty-pipelineNu.   빈 파일 처리 결과가 예상치 않음: 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~FD!9t=klrks;tt9!9~rR   c                     |dz  }|j                          |j                  dt        |             t        d      }|J d| d       y)uc   존재하지 않는 파이프라인 ID에 대해 get_pipeline_state는 None을 반환해야 한다.r   r   znonexistent-pipeline-xyzNu2   존재하지 않는 pipeline인데 None이 아닌 r   )r   r   rM   r   r  s        rG   0test_get_pipeline_state_nonexistent_returns_nonezBTestCrashRecovery.test_get_pipeline_state_nonexistent_returns_none  sR    w&	>IO#$>?~i!STZS[[hii~rR   c                 "   |dz  }|j                          |j                  dt        |             dddd}|dz  j                  t	        j
                  |             t        d      }|J d	       |j                  d
      dk(  s
J d|        y)uM   정상 JSON 파일은 get_pipeline_state가 올바르게 로드해야 한다.r   r   zrecover-test	completedr_   )r   r   
steps_donezrecover-test.jsonNu   정상 JSON인데 None 반환r   u#   status 값이 올바르지 않음: )r   r   rM   r   r   r   r   r   )rN   rO   r   r   r   r\   s         rG   2test_get_pipeline_state_valid_json_loads_correctlyzDTestCrashRecovery.test_get_pipeline_state_valid_json_loads_correctly  s    w&	>IO%3{Z[\
	(	(44TZZ
5KL#N3!B#BB!zz(#{2b6YZ`Ya4bb2rR   N)r|   r}   r~   r   r  r  r  r  r   rR   rG   r   r     s    A	yuj	crR   r   c                       e Zd ZdZd Zd Zy)TestDispatchShellFalseu3   dispatch_step의 subprocess.run shell=False 검증.c                 j   |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J d       D ]#  }|j                  dd      }|du rJ d| d        y# 1 sw Y   GxY w)uF   dispatch_step은 subprocess.run을 shell=False로 호출해야 한다.r   r   r%   zSecurity Stepr   r&   r'   r(   zsecurity-pipeliner   r   c                  >    j                  |       t        d      S )Nr   r   appendr   )r6   kwargscaptured_kwargss     rG   r   zLTestDispatchShellFalse.test_dispatch_step_uses_shell_false.<locals>.mock_run(  s    ""6***rR   r   side_effectNr   u%   subprocess.run이 호출되지 않음shellFu   shell=False가 아닌 shell=u   로 subprocess.run이 호출됨)r   rM   r   r   r   r   r   )	rN   rO   r   r   r   r   r  	shell_valr  s	           @rG   #test_dispatch_step_uses_shell_falsez:TestDispatchShellFalse.test_dispatch_step_uses_shell_false  s    >HwDV@WX	G	""$ #&"?!
 /$

 ')	+ :Q 	<$ 3U;	< ?#a'P)PP'% 	qF

7E2I%p)Ei[Po'pp%	q		< 	<s   B))B2c                 F   |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       r*d   }t        |t              sJ dt        |       d       yy# 1 sw Y   6xY w)um   dispatch_step은 subprocess.run에 문자열이 아닌 리스트를 첫 번째 인자로 전달해야 한다.r   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   r   r  )r6   r  captured_argss     rG   r   z`TestDispatchShellFalse.test_dispatch_step_passes_list_not_string_to_subprocess.<locals>.mock_runC  s=    $$T!W-6!$$VF^4**rR   r   r  Nr   u6   subprocess.run의 첫 번째 인자가 list가 아닌 u   임 (shell injection 위험))r   rM   r   r   r   r   r   r   )rN   rO   r   r   r   r   	first_argr  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GYGXXtuv  	< 	<s   BB N)r|   r}   r~   r   r  r  r   rR   rG   r  r    s    =q>vrR   r  )/r   r   r   rh   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   r   r   r   r  r   rR   rG   <module>r(     sY      	  
   0 0 
 '!HHOOA~&
    , 
#!;/ )		
  *=> &"?!	
 &"@#*!	
!! !Re  *J Jd() ()`LB LBh6' 6'~Ri Rit(c (c`?v ?vrR   