
    NiZ                     .   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mZ ddlmZ ddl	m
Z
 ddlZej                  j                  dd       ddlZej                   d        Z ej                   d	      d
        Z G d d      Z G d d      Z G d d      Zy)u  
dispatch.py 배치 추적 기능 테스트
테스터: 아르고스 (Argos)
대상: register_batch_task(), batch_status(), CLI --batch-status / --batch-id

테스트 항목:
    1. register_batch_task() - 새 배치 파일 생성, 기존 배치에 태스크 추가, JSON 구조 검증
    2. batch_status()        - 정상 조회, 존재하지 않는 batch_id, .done 파일 처리, 전체 완료 시 completed_at 갱신
    3. CLI (argparse)        - --batch-status 조회 모드, --team --task --batch-id 파싱
    N)datetime)Path)patchz/home/jay/workspacec                     | dz  dz  j                  d       | dz  dz  j                  d       | dz  dz  j                  d       | S )uG   임시 워크스페이스 생성 (memory/batches, memory/events 포함)memorybatchesTparentseventstasks)mkdir)tmp_paths    1/home/jay/workspace/teams/dev1/test_task_105_1.pytmp_workspacer   !   sZ     9$++D+98#**4*87"))$)7O    F)autousec              #   `   K   t         j                  }| t         _        |  |t         _        yw)uU   dispatch_module.WORKSPACE를 임시 디렉토리로 교체하고 테스트 후 복원N)dispatch_module	WORKSPACE)r   originals     r   patch_workspacer   *   s)      ((H -O
 (Os   ,.c                   4    e Zd ZdZd Zd Zd Zd Zd Zd Z	y)	TestRegisterBatchTasku&   register_batch_task() 단위 테스트c                     d}t        j                  |dd       |dz  dz  | dz  }|j                         s
J d|        y)	uC   새 batch_id로 호출하면 {batch_id}.json 파일이 생성된다zbatch-test-001task-1.1	dev1-teamr   r   .jsonu#   배치 파일이 생성돼야 함: N)r   register_batch_taskexists)selfr   batch_id
batch_files       r   test_1_1_creates_new_batch_filez5TestRegisterBatchTask.test_1_1_creates_new_batch_file;   sS    #++Hj+N$x/);
%>PP
  "V&I*$VV"r   c                 R   d}t        j                  |dd       t        j                  |dd       |dz  dz  | dz  }t        j                  |j	                  d	
            }t        |d         dk(  sJ d|d           |d   D cg c]  }|d   	 }}d|v sJ d|v sJ yc c}w )uU   기존 배치 파일에 태스크를 추가하면 tasks 배열 길이가 증가한다zbatch-test-002z	task-10.1r   z	task-10.2	dev2-teamr   r   r   utf-8encodingr      u'   tasks 배열에 2개가 있어야 함: task_idN)r   r   jsonloads	read_textlen)r    r   r!   r"   datattask_idss          r   'test_1_2_appends_task_to_existing_batchz=TestRegisterBatchTask.test_1_2_appends_task_to_existing_batchD   s    #++Hk;O++Hk;O$x/);
%>PP
zz*...@A4=!Q&a*QRVW^R_Q`(aa&*.w-8QAiL88h&&&h&&& 9s   	B$c                    d}t        j                  |dd       |dz  dz  | dz  }t        j                  |j	                  d            }d	|v sJ d
       d|v sJ d       d|v sJ d       d|v sJ d       |d	   |k(  sJ d|d	           y)uV   생성된 JSON에 batch_id, tasks, created_at, completed_at 필드가 있어야 한다zbatch-test-003z	task-20.1r   r   r   r   r&   r'   r!   u    batch_id 필드가 있어야 함r      tasks 필드가 있어야 함
created_atu"   created_at 필드가 있어야 함completed_atu$   completed_at 필드가 있어야 함u"   batch_id 값이 일치해야 함: Nr   r   r+   r,   r-   r    r   r!   r"   r/   s        r   +test_1_3_json_structure_has_required_fieldszATestRegisterBatchTask.test_1_3_json_structure_has_required_fieldsT   s    #++Hk;O$x/);
%>PP
zz*...@AT!E#EE!$? ??t#I%II#%M'MM%J8+d/QRVWaRbQc-dd+r   c                 @   d}t        j                  |dd       |dz  dz  | dz  }t        j                  |j	                  d            }|d	   d
   }d|v sJ d       d|v sJ d       d|v sJ d       d|v sJ d       |d   dk(  sJ |d   dk(  sJ |d   dk(  sJ y)u\   tasks 배열 내 각 항목에 task_id, team, status, report_path 필드가 있어야 한다zbatch-test-004z	task-30.1r%   r   r   r   r&   r'   r   r   r*   u   task_id 필드가 있어야 함teamu   team 필드가 있어야 함statusu   status 필드가 있어야 함report_pathu#   report_path 필드가 있어야 함
dispatchedNr7   )r    r   r!   r"   r/   
task_entrys         r   test_1_4_task_entry_structurez3TestRegisterBatchTask.test_1_4_task_entry_structurec   s    #++Hk;O$x/);
%>PP
zz*...@A']1%
J&I(II&#C%CC#:%G'GG%
*Q,QQ*)$333&![000(#|333r   c                     d}t        j                  |dd       |dz  dz  | dz  }t        j                  |j	                  d            }|d	   J d|d	           y
)uG   신규 배치 파일 생성 직후 completed_at은 None이어야 한다zbatch-test-005z	task-40.1	dev3-teamr   r   r   r&   r'   r6   Nu4   신규 배치의 completed_at은 None이어야 함: r7   r8   s        r   )test_1_5_completed_at_is_none_on_creationz?TestRegisterBatchTask.test_1_5_completed_at_is_none_on_creationu   sz    #++Hk;O$x/);
%>PP
zz*...@AN#+ 	ZB4CWBXY	Z+r   c                     d}d}t        j                  ||d       |dz  dz  | dz  }t        j                  |j	                  d            }|d	   d
   d   }|d| dk(  s
J d|        y)uC   report_path는 'memory/reports/{task_id}.md' 형식이어야 한다zbatch-test-006z	task-50.1r   r   r   r   r&   r'   r   r   r=   memory/reports/.mdu+   report_path 형식이 올바르지 않음: Nr7   )r    r   r!   r*   r"   r/   r=   s          r   test_1_6_report_path_formatz1TestRegisterBatchTask.test_1_6_report_path_format   s    #++Hg{K$x/);
%>PP
zz*...@A7mA&}5y<< 	H9+G	H<r   N)
__name__
__module____qualname____doc__r#   r2   r9   r@   rC   rG    r   r   r   r   7   s'    0W' e4$	ZHr   r   c                   N    e Zd ZdZddZd Zd Zd Zd Zd Z	d	 Z
d
 Zd Zd Zy)TestBatchStatusu   batch_status() 단위 테스트Nc                     |dz  dz  }|j                  dd       ||t        j                         j                         |d}|| dz  }|j	                  t        j                  |dd	      d
       |S )u(   테스트용 배치 파일 직접 생성r   r   Tr
   exist_okr!   r   r5   r6   r   Fr)   ensure_asciiindentr&   r'   )r   r   now	isoformat
write_textr+   dumps)r    r   r!   r   r6   batches_dirr/   r"   s           r   _create_batch_filez"TestBatchStatus._create_batch_file   s    #h.:$6 ",,.224(	
 !hZu#55
djjE!LW^_r   c           	         d}| j                  ||dddddg       t        j                  |      }d|vs|j                  d      dk7  s
J d	|        |d
   |k(  s
J d|        d|v sJ d       t	        |d         dk(  sJ y)uF   정상적인 batch_id로 조회하면 배치 데이터가 반환된다zbatch-status-001r   r   r>   zmemory/reports/task-1.1.mdr*   r;   r<   r=   r<   erroru)   정상 조회인데 에러가 반환됨: r!   u   batch_id가 일치해야 함: r   r4      N)r[   r   batch_statusgetr.   r    r   r!   results       r   "test_2_1_normal_batch_status_queryz2TestBatchStatus.test_2_1_normal_batch_status_query   s    %X#[L9; <	
 !--h7v%H)=)H 	A7x@	AHj!X-X1OPVx/XX-& A"AA 6'?#q(((r   c                 h    t        j                  d      }|d   dk(  s
J d|        d|v sJ d       y)uT   존재하지 않는 batch_id로 조회하면 status=error와 message가 반환된다zbatch-nonexistent-999r<   r^   u;   존재하지 않는 batch_id이면 status=error여야 함: messageu)   error 시 message 필드가 있어야 함N)r   r`   )r    r   rc   s      r   +test_2_2_nonexistent_batch_id_returns_errorz;TestBatchStatus.test_2_2_nonexistent_batch_id_returns_error   sM     --.EFh7* 	SI&R	S*F"O$OO"r   c           
         d}d}| j                  |||ddd| ddg       |dz  d	z  }|j                  d
d
       || dz  }|j                  dd       t        j                  |      }|d   d   }|d   dk(  s
J d|        y)uJ   .done 파일이 존재하는 태스크는 status=completed로 반영된다zbatch-done-001ztask-done-1.1r   r>   rE   rF   r]   r   r   TrP   .donedoner&   r'   r   r   r<   	completedu6   .done 파일이 있으면 status=completed여야 함: N)r[   r   rX   r   r`   )r    r   r!   r*   
events_dir	done_filerc   task_results           r   'test_2_3_done_file_marks_task_completedz7TestBatchStatus.test_2_3_done_file_marks_task_completed   s    #!X +.wis;= >	
 %x/(:
5G9E!22	Vg6 --h7Woa(8$3 	SD[MR	S3r   c                 R   d}d}d}| j                  |||ddd| dd|d	dd| ddgd
       |dz  dz  }|| dz  j                  d       || dz  j                  d       t        j                  |      }|d   
J d|        t	        |d         dk\  sJ d|d           y
)uR   모든 태스크가 completed 상태이고 completed_at이 None이면 갱신된다zbatch-alldone-001ztask-alldone-1.1ztask-alldone-1.2r   r>   rE   rF   r]   r%   Nr6   r   r   ri   rj   r6   u>   모든 태스크 완료 시 completed_at이 갱신돼야 함:    u)   completed_at이 ISO 형식이어야 함: )r[   rX   r   r`   r.   )r    r   r!   	task_id_1	task_id_2rl   rc   s          r   )test_2_4_all_tasks_done_sets_completed_atz9TestBatchStatus.test_2_4_all_tasks_done_sets_completed_at   s   &&	&	X%{l"1)C @B%{l"1)C @B  	  		
 %x/(:
	5)	)55f=	5)	)55f= --h7n%1 	VLVHU	V1 6.)*b0 	Q7~8N7OP	Q0r   c                     d}d}d}| j                  |||ddd| dd|d	dd| ddgd
       |dz  dz  }|| dz  j                  d       t        j                  |      }|d   
J d|        y
)uL   일부 태스크만 완료된 경우 completed_at은 None으로 유지된다zbatch-partial-001ztask-partial-1.1ztask-partial-1.2r   r>   rE   rF   r]   r%   Nrq   r   r   ri   rj   r6   uK   일부 미완료 태스크가 있으면 completed_at이 None이어야 함: )r[   rX   r   r`   )r    r   r!   task_id_donetask_id_pendingrl   rc   s          r   3test_2_5_partial_completion_keeps_completed_at_nonezCTestBatchStatus.test_2_5_partial_completion_keeps_completed_at_none   s    &),X(+"1,s CE+[L"1/1B# FH  	  		
 %x/(:
	e,	,88@ --h7n%- 	cYZ`Yab	c-r   c           
         d}d}| j                  |||ddd| ddg       d|d	d
iii}|dz  dz  }|j                  t        j                  |dd             t	        j
                  |      }|d   d   }|d	   d
k(  s
J d|        y)uW   task-timers.json에 running 상태가 있으면 해당 태스크 status에 반영된다zbatch-timer-001ztask-timer-1.1r   r>   rE   rF   r]   r   r<   runningr   task-timers.jsonFr)   rS   r   u4   timer에 running이면 status=running이어야 함: Nr[   rX   r+   rY   r   r`   )r    r   r!   r*   
timer_data
timer_filerc   rn   s           r   1test_2_6_timer_status_reflected_when_no_done_filezATestBatchStatus.test_2_6_timer_status_reflected_when_no_done_file  s    $"X +.wis;= >	
 (I)>?@
$x/2DD
djj%PQRS --h7Woa(8$	1 	QB;-P	Q1r   c           
      P   d}d}| j                  |||ddd| ddg       d|d	d
iii}|dz  dz  }|j                  t        j                  |dd             |dz  dz  }|| dz  j                  d       t	        j
                  |      }|d   d   }|d	   dk(  s
J d|        y)uU   .done 파일이 있으면 task-timers.json 상태와 무관하게 completed가 된다zbatch-priority-001ztask-priority-1.1r   r>   rE   rF   r]   r   r<   r{   r   r|   Fr)   rS   r   ri   rj   r   rk   u.   .done 파일이 timer보다 우선해야 함: Nr}   )	r    r   r!   r*   r~   r   rl   rc   rn   s	            r   ,test_2_7_done_file_takes_priority_over_timerz<TestBatchStatus.test_2_7_done_file_takes_priority_over_timer%  s    '%X +.wis;= >	
 (I)>?@
$x/2DD
djj%PQRS %x/(:
		'	'33F; --h7Woa(8$3 	K<[MJ	K3r   c                     d}| j                  ||g        t        j                  |      }|j                  d      dk7  s
J d|        |d   |k(  sJ |d   g k(  sJ y)uC   tasks 배열이 비어있는 배치도 정상적으로 조회된다zbatch-empty-001r<   r^   u*   빈 배치도 에러가 아니어야 함: r!   r   N)r[   r   r`   ra   rb   s       r   test_2_8_empty_tasks_batchz*TestBatchStatus.test_2_8_empty_tasks_batch?  sv    $2> --h7 zz(#w.e2\]c\d0ee.j!X---g"$$$r   c                 V   d}d}d}| j                  |||ddd| dd|d	dd| ddgd
       d|ddiii}|dz  dz  }|j                  t        j                  |dd             |dz  dz  }|| dz  j                  d       t	        j
                  |      }|d   
J d|        y
)uN   cancelled 태스크는 completed와 동일하게 all_completed에 포함된다zbatch-cancelled-001ztask-cancel-1.1ztask-cancel-1.2r   r>   rE   rF   r]   r%   Nrq   r   r<   	cancelledr   r|   Fr)   rS   r   ri   rj   r6   uE   cancelled + completed 조합이면 completed_at이 갱신돼야 함: r}   )	r    r   r!   task_id_cancelledrw   r~   r   rl   rc   s	            r   &test_2_9_cancelled_counts_as_completedz6TestBatchStatus.test_2_9_cancelled_counts_as_completedL  s   (-(X-{l"12C1DC HJ(+"1,s CE  	  		
  1Hk3JKL
$x/2DD
djj%PQRS %x/(:
	e,	,88@ --h7 n%1 	]STZS[\	]1r   )N)rH   rI   rJ   rK   r[   rd   rg   ro   ru   ry   r   r   r   r   rL   r   r   rN   rN      s>    ))$PS.Q<c4Q,K4
%]r   rN   c                   2    e Zd ZdZdZd Zd Zd Zd Zd Z	y)	TestCLIu:   CLI 테스트 (subprocess integration 및 argparse 파싱)z/home/jay/workspace/dispatch.pyc                 r   |dz  dz  }|j                  d       |dz  dz  j                  d       d}|ddd	d
dgt        j                         j                         dd}|| dz  }|j	                  t        j                  |dd             t        j                  j                         }t        |      |d<   t        j                  d| j                  d|gdd|      }|j                  dk(  sJ d|j                          t        j                   |j"                        }|d   |k(  s
J d|        y)u=   --batch-status 옵션으로 실행하면 JSON이 출력된다r   r   Tr	   r   zbatch-cli-001ztask-cli-1.1r   r>   zmemory/reports/task-cli-1.1.mdr]   NrR   r   Fr)   rS   WORKSPACE_ROOTpython3--batch-statuscapture_outputtextenvr   uA   --batch-status 실행이 성공해야 함 (returncode=0): stderr=r!   u(   출력된 batch_id가 일치해야 함: )r   r   rV   rW   rX   r+   rY   osenvironcopystr
subprocessrunDISPATCH_PY
returncodestderrr,   stdout)	r    r   rZ   r!   
batch_datar"   r   procoutputs	            r   &test_3_1_batch_status_cli_returns_jsonz.TestCLI.test_3_1_batch_status_cli_returns_jsonv  sY    )I5$'	H	x	'..t.<" *K'8XZ #,,.224 

 !hZu#55
djj%PQRSjjoo #H~~((*:HEd

 !# 	^OPTP[P[}]	^# DKK(j!X- 	@6vh?	@-r   c                    |dz  dz  j                  d       |dz  dz  j                  d       t        j                  j                         }t	        |      |d<   t        j                  d| j                  dd	gdd|
      }|j                  dk(  sJ d|j                          t        j                  |j                        }|d   dk(  s
J d|        y)uV   존재하지 않는 batch_id로 --batch-status 실행하면 error JSON이 출력된다r   r   Tr	   r   r   r   r   zbatch-not-exist-99999r   r   u?   returncode=0이어야 함 (error는 JSON으로 출력): stderr=r<   r^   u9   존재하지 않는 배치이면 status=error여야 함: N)r   r   r   r   r   r   r   r   r   r   r+   r,   r   )r    r   r   r   r   s        r   3test_3_2_batch_status_cli_nonexistent_returns_errorz;TestCLI.test_3_2_batch_status_cli_nonexistent_returns_error  s    	H	y	(///=	H	x	'..t.<jjoo #H~~((*:<STd

 !# 	\Mdkk][	\# DKK(h7* 	QGxP	Q*r   c                 .   ddl }|j                         }|j                  dg d       |j                  d       |j                  ddg d	
       |j                  ddd       |j                  dddd       |j                  g d      }|j                  dk(  sJ d|j                          |j
                  dk(  sJ d|j
                          |j                  dk(  sJ d|j                          |j                  J d|j                          y)uM   --team, --task, --batch-id 옵션이 argparse에서 올바르게 파싱된다r   N--teamr   r%   rB   choices--taskz--levelnormal)r   criticalsecurity)defaultr   
--batch-idr!   r   destr   r`   BATCH_IDr   r   metavar)r   r   r      API 서버 구축r   batch-20260302-001r   u   team 파싱 오류: r   u   task 파싱 오류: r   u   batch_id 파싱 오류: u#   batch_status는 None이어야 함: )argparseArgumentParseradd_argument
parse_argsr;   taskr!   r`   r    r   parserargss       r   (test_3_3_argparse_batch_id_option_parsedz0TestCLI.test_3_3_argparse_batch_id_option_parsed  s3    ((*H.UVH%Ix$F 	 	HL$ZH,d$. 	 	0    "
  yyK'K+?		{)KK'yy//S3G		{1SS/}} 44`8PQUQ^Q^P_6``4  (c,OPTPaPaOb*cc(r   c                    ddl }|j                         }|j                  dg d       |j                  d       |j                  ddd	       |j                  d
ddd       |j                  d
dg      }|j                  dk(  sJ d|j                          |j
                  J d       |j                  J d       y)u:   --batch-status 단독 사용 시 올바르게 파싱된다r   Nr   r   r   r   r   r!   r   r   r`   r   r   r   u   batch_status 파싱 오류: u3   batch-status 모드에서 team은 None이어야 함u3   batch-status 모드에서 task는 None이어야 함)r   r   r   r   r`   r;   r   r   s       r   ,test_3_4_argparse_batch_status_option_parsedz4TestCLI.test_3_4_argparse_batch_status_option_parsed  s    ((*H.UVH%L$ZH,d$. 	 	0   "24H!IJ  $88 	?*4+<+<*=>	?8yy W"WW yy W"WW r   c                 L   |dz  dz  j                  d       |dz  dz  j                  d       t        j                  j                         }t	        |      |d<   t        j                  d| j                  gdd|      }|j                  d	k7  sJ d
|j                          y)u]   --team / --task / --batch-status 없이 실행하면 0이 아닌 종료코드를 반환한다r   r   Tr	   r   r   r   r   r   uI   --team --task 없이 실행하면 비정상 종료해야 함: returncode=N)	r   r   r   r   r   r   r   r   r   )r    r   r   r   s       r   .test_3_5_cli_exits_error_without_team_and_taskz6TestCLI.test_3_5_cli_exits_error_without_team_and_task  s    	H	y	(///=	H	x	'..t.<jjoo #H~~(()d

 !# 	jWX\XgXgWhi	j#r   N)
rH   rI   rJ   rK   r   r   r   r   r   r   rL   r   r   r   r   p  s)    D3K!@HQ*d4X&jr   r   )rK   r+   r   r   systempfiler   pathlibr   unittest.mockr   pytestpathinsertdispatchr   fixturer   r   r   rN   r   rL   r   r   <module>r      s   	  	  
      ( ) "   ) )UH UHxV] V]zzj zjr   