
    Z1i                        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mZmZ ddlmZ ddlmZmZmZ ddlZe
j,                  j/                  dd       ej0                  d        Zej0                  d        Zej0                  d	        Zdd
Z G d d      Z G d d      Z G d d      Z G d d      Z  G d d      Z!y)u   
dispatch.py 및 task-timer.py 재발 방지 로직 테스트

아르고스(테스터)가 작성한 테스트 스위트.
dispatch.py의 팀 가용성 확인, 이중 위임 방지,
task-timer.py의 check-reserved 기능을 검증.
    N)datetime	timedelta)Path)	MagicMockpatch	mock_openz/home/jay/workspacec                     | dz  }|j                  d       |dz  }|j                  d       | dz  }|j                  d       |dz  }|j                  d       | S )u-   임시 워크스페이스 디렉토리 생성memoryT)parentstasksprojectsdaily)mkdir)tmp_path
memory_dir	tasks_dirprojects_dir	daily_dirs        :/home/jay/workspace/teams/dev1/test_dispatch_safeguards.pytmp_workspacer      so     H$JT"W$IOODO!j(Lt$W$IOODO!O    c                     ddddddt        j                         j                         dii}| dz  dz  }|j                  t	        j
                  |d	d
             |S )u0   running 작업이 있는 task-timers.json 생성r   z	task-10.1	dev1-teamu   로그인 페이지 개발runningtask_idteam_iddescriptionstatus
start_timer
   task-timers.jsonF   ensure_asciiindentr   now	isoformat
write_textjsondumpsr   
timer_data
timer_files      r   timer_file_with_runningr/   +   sl     	&&;#&lln668

J ),>>J$**ZeANOr   c           	          ddddddt        j                         j                         t        j                         j                         dii}| dz  dz  }|j                  t	        j
                  |d	d
             |S )u2   completed 작업만 있는 task-timers.json 생성r   ztask-9.1r   u   이전 작업 완료	completed)r   r   r   r   r    end_timer
   r!   Fr"   r#   r&   r,   s      r   timer_file_with_completedr3   >   s~     	%&5%&lln668$LLN446	
J ),>>J$**ZeANOr   c                 f     #t        dt        j                  ddi      d      	 	 d fd	}|S )uc   dispatch() 함수를 임시 워크스페이스 환경에서 실행하도록 패치된 버전 반환r   idzcron-test-123 
returncodestdoutstderrc                    dd l }|j                  }|j                  j                         }		 |_        ddddd|_        t	        d      5  t	        d	d
      5  dz  dz  }
|
j                         r$t        j                  |
j                               }ndi i}dt        j                         j                         d|d   d
<   |
j                  t        j                  |dd             |j                  | ||||||      cd d d        cd d d        ||_        |	|_        S # 1 sw Y   nxY w	 d d d        n# 1 sw Y   nxY w||_        |	|_        y # ||_        |	|_        w xY w)Nr   ztest-anu-keyztest-dev1-keyztest-dev2-keyztest-dev3-keyanudev1dev2dev3dispatch.subprocess.runreturn_valuedispatch.generate_task_id	task-99.1r
   r!   r   reservedr   reserved_atFr"   r#   )
session_id
project_idforceverify)dispatch	WORKSPACEBOT_KEYScopyr   existsr*   loads	read_textr   r'   r(   r)   r+   )r   	task_desclevelrI   rJ   rK   rL   dispatch_moduleoriginal_workspaceoriginal_bot_keysr.   tdmock_subprocess_resultr   s               r   patched_dispatchz,make_dispatch_func.<locals>.patched_dispatch[   s~   *,66+4499; 	9(5O%%'''	(O$ 0?UV 6[Q !.!9<N!NJ!((*!ZZ
(<(<(>?%r]",'/||~'?'?'A0BwK, ))$**ReTU*VW*33E#-#-#% 4   , );O%'8O$-    , );O%'8O$ );O%'8O$sB    E D<B*D&	D<	E &D/	+D<3	E <EE E')normalNNFF)r   r*   r+   )r   rZ   r[   s   `` r   make_dispatch_funcr]   R   sC    %!*::t_56"
 IM>C'9R r   c                   (    e Zd ZdZd Zd Zd Zd Zy)TestTeamAvailabilityu+   dispatch.py: 팀 가용성 확인 테스트c                    ddl }|j                  }	 ||_        t        |j                   d      r|j                   j                  dd      nd}|@|j                  j                         }ddddd|_        	 |j                  dd      }||_        ||_        |d   }d	}||k(  }	|	st        j                  d
|	fd||f      t        j                  |      t        j                  |      dz  }
t        j                  d|       dz   d|
iz  }t        t        j                  |            dx}x}	}d}|d   }||v }	|	st        j                  d|	fd||f      t        j                  |      t        j                  |      dz  }
t        j                  d|d          dz   d|
iz  }t        t        j                  |            dx}x}	}y# ||_        w xY w# ||_        w xY w)uW   1a: team_id에 running 작업이 있을 때 에러 반환 (message에 '--force' 포함)r   N__wrapped__r   u   새 작업 설명test-keyr<   r   error==z%(py1)s == %(py4)spy1py4u.   running 작업이 있을 때 error여야 함: 
>assert %(py6)spy6z--forcemessageinz%(py1)s in %(py4)su*   message에 '--force'가 포함돼야 함: )rM   rN   hasattrra   rO   rP   
@pytest_ar_call_reprcompare	_saferepr_format_assertmsgAssertionError_format_explanation)selfr   r/   rV   rW   resultrX   @py_assert0@py_assert3@py_assert2@py_format5@py_format7s               r   $test_1a_running_task_blocks_dispatchz9TestTeamAvailability.test_1a_running_task_blocks_dispatch   s   *,66	;(5O% 11=A %--990GK  ~$3$<$<$A$A$C!%z&
,(A,55kCVWF/@O,(:O%he7e7*eee7eeeeee7eee.\]c\d,eeeeeeeeoF9-oy--oooy-oooyooo-ooo1[\bcl\m[n/oooooooo 0AO,(:O%s$   A#G 6G G 	GG 	G c           	         ddl }t        dt        j                  ddi      d      }|j                  }|j
                  j                         }	 ||_        ddddd|_        t        d	|
      5  t        dd
      5  t        j                  |j                               }dt        j                         j                         d|d   d<   |j                  t        j                  |dd             |j                  ddd      }ddd       ddd       ||_        ||_        d   }	d}
|	|
k(  }|st        j                  d|fd|	|
f      t        j                   |	      t        j                   |
      dz  }t        j"                  d|       dz   d|iz  }t%        t        j&                  |            dx}	x}}
y# 1 sw Y   xY w# 1 sw Y   xY w# ||_        ||_        w xY w)uK   1b: running 작업이 있고 force=True → 정상 진행 (mock subprocess)r   Nr5   zcron-abc-123r6   r7   rb   r<   rA   rB   rD   rE   rF   rG   r   Fr"   r#   r   u   강제 위임 작업TrK   r   
dispatchedrd   rf   rg   u'   force=True이면 dispatched여야 함: rj   rk   rM   r   r*   r+   rN   rO   rP   r   rR   rS   r   r'   r(   r)   rq   rr   rs   rt   ru   rv   )rw   r   r/   rV   mock_resultrW   rX   rY   rx   ry   rz   r{   r|   r}   s                 r   (test_1b_running_task_with_force_proceedsz=TestTeamAvailability.test_1b_running_task_with_force_proceeds   s   *::t^45
 -66+4499;	9(5O%!:"J(O$ 0{K 6[Q $;$E$E$GHB",'/||~'?'?'A0BwK, ,66

2E!D -55#%;4 6 F );O%'8O$h 	?< 	?</ 	?->->	?< 	? 	?5>Y   	? 	?5>Y $0 	? 	?->->5fX>	? 	? 	?+>+>	? 	? 	?#   );O%'8O$s=    G /G=B
F?GG ?G	GGG G'c           	         ddl }t        dt        j                  ddi      d      }|j                  }|j
                  j                         }	 ||_        ddddd|_        t        d	|
      5  t        dd
      5  t        j                  |j                               }dt        j                         j                         d|d   d<   |j                  t        j                  |dd             |j                  dd      }ddd       ddd       ||_        ||_        d   }	d}
|	|
k(  }|st        j                  d|fd|	|
f      t        j                   |	      t        j                   |
      dz  }t        j"                  d|       dz   d|iz  }t%        t        j&                  |            dx}	x}}
y# 1 sw Y   xY w# 1 sw Y   xY w# ||_        ||_        w xY w)u;   1c: team_id에 completed 작업만 있을 때 정상 진행r   Nr5   zcron-xyz-456r6   r7   rb   r<   rA   rB   rD   rE   rF   rG   r   Fr"   r#   r   u$   새 작업 (이전 completed 상태)r   r   rd   rf   rg   u5   completed 작업만 있을 때 dispatched여야 함: rj   rk   r   )rw   r   r3   rV   r   rW   rX   rY   rx   ry   rz   r{   r|   r}   s                 r   &test_1c_completed_task_allows_dispatchz;TestTeamAvailability.test_1c_completed_task_allows_dispatch   s   *::t^45
 -66+4499;	9(5O%!:"J(O$ 0{K 6[Q $=$G$G$IJB",'/||~'?'?'A0BwK, .88

2E!D -55#%KF );O%'8O$h 	M< 	M</ 	M;L;L	M< 	M 	MCL9   	M 	MCL9 $0 	M 	M;L;LCF8L	M 	M 	M9L9L	M 	M 	M!   );O%'8O$s=    G /G	=BF=G	G =G	G		GG G%c           	         ddl }|dz  dz  }|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}x}}t        dt        j                  d	d
i      d      }|j                  }	|j                  j                         }
	 ||_        ddddd|_        t!        d|      5  t!        dd      5  dddt#        j$                         j'                         dii}|j)                  t        j                  |dd             |j                  dd      }ddd       ddd       |	|_        |
|_        d   }d}||k(  }|st        j*                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d |       d!z   d"|iz  }t        t        j                  |            dx}x}}y# 1 sw Y   xY w# 1 sw Y   xY w# |	|_        |
|_        w xY w)#u0   1d: task-timers.json이 없을 때 정상 진행r   Nr
   r!   u   timer 파일이 없어야 함zG
>assert not %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}r.   )py0py2ri   r5   zcron-new-789r6   r7   rb   r<   rA   rB   rD   rE   r   rF   rG   Fr"   r#   r   u+   timer 파일 없는 상태에서 첫 위임r   r   rd   rf   rg   u1   timer 파일 없을 때도 dispatched여야 함: rj   rk   )rM   rQ   rq   rt   @py_builtinslocals_should_repr_global_namers   ru   rv   r   r*   r+   rN   rO   rP   r   r   r'   r(   r)   rr   )rw   r   rV   r.   @py_assert1rz   @py_assert5@py_format6r   rW   rX   rY   rx   ry   r{   r|   r}   s                    r   %test_1d_no_timer_file_allows_dispatchz:TestTeamAvailability.test_1d_no_timer_file_allows_dispatch   s\   * #X-0BB
$$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G::t^45
 -66+4499;	9(5O%!:"J(O$ 0{K 
6[Q 	!K",'/||~'?'?'A2 $ B ))$**ReTU*VW,55#%RF	
 );O%'8O$h 	I< 	I</ 	I7H7H	I< 	I 	I?Hy   	I 	I?Hy $0 	I 	I7H7H?xH	I 	I 	I5H5H	I 	I 	I	 	
 
 );O%'8O$s=    I9 8I-A#I!)I-1I9 !I*	&I--I62I9 9J	N)__name__
__module____qualname____doc__r~   r   r   r    r   r   r_   r_      s    5p8%?N$ML&Ir   r_   c                   p    e Zd ZdZej
                  d        Zej
                  d        Zd Zd Z	d Z
d Zy)	TestDoubleDispatchPreventionu+   dispatch.py: 이중 위임 방지 테스트c                     d}dddd|dd dt        j                         j                         dii}|d	z  d
z  }|j                  t	        j
                  |dd             ||fS )u  running 상태에 특정 description이 있는 timer 파일 생성.

        description 중복 검사(2단계)를 테스트하기 위해:
        - 팀 가용성 검사(1단계): team_id가 'dev1-team'인 running 작업만 차단
        - 중복 description 검사(2단계): 다른 팀(dev2-team) running 작업의 description과 같은지 비교
        따라서 fixture는 dev1-team과 다른 팀(dev2-team)에 running 작업을 두고,
        dev1-team으로 dispatch 시 description 중복만 걸리도록 설정.
        실제로는 같은 팀 running 작업이 아닌 reserved 상태로 description 중복 테스트가 명확함.
        u\   동일 작업 - 이미 예약된 로그인 페이지 개발 프로젝트입니다 (reserved)r   z	task-11.1r   N<   rF   r   r   r   r   rH   r
   r!   Fr"   r#   r&   rw   r   descr-   r.   s        r   timer_with_running_descz4TestDoubleDispatchPrevention.timer_with_running_desc'  s~     n**#'9(#+<<>#;#;#=	

 #X-0BB
djj%PQRS4r   c                     d}dddd|dd dt        j                         j                         dii}|d	z  d
z  }|j                  t	        j
                  |dd             ||fS )uC   reserved 상태에 특정 description이 있는 timer 파일 생성uN   동일 작업 - 이미 예약된 API 서버 구축 작업입니다 (예약됨)r   z	task-12.1r   Nr   rF   r   r
   r!   Fr"   r#   r&   r   s        r   timer_with_reserved_descz5TestDoubleDispatchPrevention.timer_with_reserved_descC  s~     `**#'9(#+<<>#;#;#=


 #X-0BB
djj%PQRS4r   c                 X   ddl }|\  }}|j                  }|j                  j                         }	 ||_        ddddd|_        |j                  d|      }||_        ||_        |d   }	d}
|	|
k(  }|st	        j
                  d|fd	|	|
f      t	        j                  |	      t	        j                  |
      d
z  }t	        j                  d|       dz   d|iz  }t        t	        j                  |            dx}	x}}
d}	|d   }
|	|
v }|st	        j
                  d|fd|	|
f      t	        j                  |	      t	        j                  |
      d
z  }t	        j                  d|d          dz   d|iz  }t        t	        j                  |            dx}	x}}
y# ||_        ||_        w xY w)u  2a: 같은 description(첫 60자)이 running/reserved에 있을 때 에러 반환.

        dispatch 코드 로직:
        1단계 - 팀 가용성 확인: team_id 일치 + status=running 인 작업이 있으면 차단
        2단계 - 이중 위임 방지: status=running/reserved 이고 description[:60] 동일하면 차단

        reserved 상태 작업으로 테스트하여 1단계를 우회하고 2단계(description 중복)만 검사.
        r   Nrb   r<   r   r   rc   rd   rf   rg   u*   중복 description이면 error여야 함: rj   rk   "   이미 진행 중인 동일 작업rl   rm   ro   .   message에 중복 안내가 포함돼야 함: 
rM   rN   rO   rP   rq   rr   rs   rt   ru   rv   )rw   r   r   rV   r.   r   rW   rX   rx   ry   rz   r{   r|   r}   s                 r   -test_2a_duplicate_description_blocks_dispatchzJTestDoubleDispatchPrevention.test_2a_duplicate_description_blocks_dispatchV  s    	+2
D,66+4499;
	9(5O%!:"J(O$
 %--k4@F(:O%'8O$h 	B7 	B7* 	B0A0A	B7 	B 	B8A	   	B 	B8A	 $+ 	B 	B0A0A8A	B 	B 	B.A.A	B 	B3 	Qvi7H 	Q37HH 	Q?P?P	Q37H 	Q 	QGPy 4 	Q 	QGPy 8I 	Q 	Q?P?P<VI=N<OP	Q 	Q 	Q=P=P	Q 	Q 	Q );O%'8O$s   %F F)c                 "   ddl }|\  }}|j                  }|j                  j                         }	 ||_        ddddd|_        |j                  d|      }||_        ||_        |d   }	d}
|	|
k(  }|st	        j
                  d|fd	|	|
f      t	        j                  |	      t	        j                  |
      d
z  }t	        j                  d|       dz   d|iz  }t        t	        j                  |            dx}	x}}
y# ||_        ||_        w xY w)uI   2a-reserved: reserved 상태에도 동일 description이면 에러 반환r   Nrb   r<   r   r   rc   rd   rf   rg   u:   reserved 상태 중복 description이면 error여야 함: rj   rk   r   )rw   r   r   rV   r.   r   rW   rX   rx   ry   rz   r{   r|   r}   s                 r   -test_2a_reserved_duplicate_description_blockszJTestDoubleDispatchPrevention.test_2a_reserved_duplicate_description_blocksv  s0   *3
D,66+4499;		9(5O%!:"J(O$ %--k4@F(:O%'8O$h 	R7 	R7* 	R@Q@Q	R7 	R 	RHQ	   	R 	RHQ	 $+ 	R 	R@Q@QHQ	R 	R 	R>Q>Q	R 	R 	R );O%'8O$s   %C> >Dc                    ddl }d}d||dddt        j                         j                         dii}|d	z  d
z  }|j	                  t        j                  |dd             |j                  }|j                  j                         }	 ||_        ddddd|_        d}|j                  d|      }	||_        ||_        |	d   }
d}|
|k(  }|st        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }t        j                  d|	       dz   d|iz  }t        t        j                  |            dx}
x}}d}
|	d   }|
|v }|st        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }t        j                  d|	d          dz   d|iz  }t        t        j                  |            dx}
x}}y# ||_        ||_        w xY w)u  2b: 프롬프트에 기존 task_id가 포함될 때 에러 반환.

        dispatch 코드 로직:
        1단계 - 팀 가용성 확인: team_id 일치 + running 작업 차단
        2단계 - 이중 위임 방지: 프롬프트에 기존 task_id(running/reserved) 문자열 포함 시 차단

        다른 팀(dev1-team)에 running 작업을 두고, dev2-team으로 dispatch하면서
        프롬프트에 task_id 포함 → 1단계 우회, 2b 검사에서 차단.
        r   Nz	task-13.1r   r   u   기존 작업r   r   r
   r!   Fr"   r#   rb   r<   u4   task-13.1에 대한 후속 작업을 진행하세요	dev2-teamr   rc   rd   rf   rg   u+   task_id 참조 포함 시 error여야 함: rj   rk   r   rl   rm   ro   r   )rM   r   r'   r(   r)   r*   r+   rN   rO   rP   rq   rr   rs   rt   ru   rv   )rw   r   rV   existing_task_idr-   r.   rW   rX   task_desc_with_idrx   ry   rz   r{   r|   r}   s                  r   )test_2b_task_id_in_prompt_blocks_dispatchzFTestDoubleDispatchPrevention.test_2b_task_id_in_prompt_blocks_dispatch  sE    	+ ' /*#2'"*,,.":":"<#


 #X-0BB
djj%PQRS,66+4499;	9(5O%!:"J(O$
 #W$--k;LMF(:O%'8O$h 	C7 	C7* 	C1B1B	C7 	C 	C9B   	C 	C9B $+ 	C 	C1B1B9&B	C 	C 	C/B/B	C 	C3 	Qvi7H 	Q37HH 	Q?P?P	Q37H 	Q 	QGPy 4 	Q 	QGPy 8I 	Q 	Q?P?P<VI=N<OP	Q 	Q 	Q=P=P	Q 	Q 	Q );O%'8O$s   	'G3 3Hc           	         ddl }|\  }}t        dt        j                  ddi      d      }|j                  }|j
                  j                         }	 ||_        ddddd|_        t        d	|
      5  t        dd
      5  t        j                  |j                               }	dt        j                         j                         d|	d   d<   |j                  t        j                  |	dd             |j                  d|d      }
ddd       ddd       ||_        ||_        
d   }d}||k(  }|st        j                  d|fd||f      t        j                   |      t        j                   |      dz  }t        j"                  d|
       dz   d|iz  }t%        t        j&                  |            dx}x}}y# 1 sw Y   xY w# 1 sw Y   xY w# ||_        ||_        w xY w)u7   2c: force=True로 중복 작업도 강제 진행 가능r   Nr5   zcron-force-001r6   r7   rb   r<   rA   rB   rD   rE   rF   rG   r   Fr"   r#   r   Tr   r   r   rd   rf   rg   u2   force=True 중복 작업도 dispatched여야 함: rj   rk   r   )rw   r   r   rV   r.   r   r   rW   rX   rY   rx   ry   rz   r{   r|   r}   s                   r   &test_2c_force_bypasses_duplicate_checkzCTestDoubleDispatchPrevention.test_2c_force_bypasses_duplicate_check  s   *2
D::t%567
 -66+4499;	9(5O%!:"J(O$ 0{K 
6[Q 	J$8$8$:;B",'/||~'?'?'A0BwK, ))$**ReTU*VW,55#T 6 F	
 );O%'8O$h 	J< 	J</ 	J8I8I	J< 	J 	J@I	   	J 	J@I	 $0 	J 	J8I8I@I	J 	J 	J6I6I	J 	J 	J	 	
 
 );O%'8O$s=    G 4GB
GGG G		GGG G,N)r   r   r   r   pytestfixturer   r   r   r   r   r   r   r   r   r   r   $  sM    5^^   6 ^^   $Q@R,.Q`$Jr   r   c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestCheckReservedu.   task-timer.py: check-reserved 기능 테스트c                 h   ddl }|j                  j                  dd      }|x}|j                  _        |j                  j	                  |      }|j
                  j                  |       |dz  dz  }|j                  t        j                  |dd	             |j                  t        |      
      S )uR   임시 워크스페이스에 timer 파일 생성 후 TaskTimer 인스턴스 반환r   N
task_timer(/home/jay/workspace/memory/task-timer.pyr
   r!   Fr"   r#   workspace_path)importlib.utilutilspec_from_file_locationload_from_specmodule_from_specloaderexec_moduler)   r*   r+   	TaskTimerstr)rw   r   r-   	importlibspectask_timer_modulemodr.   s           r   _make_timerzTestCheckReserved._make_timer  s     	~~556
 =A@INN9nn--d3$"X-0BB
djj%PQRS}}C,>}??r   c                    t        j                         t        d      z
  j                         }dddddd|dii}| j	                  ||      }|j                  d	
      }|d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	t        j                  d|       dz   d|	iz  }
t        t        j                  |
            dx}x}}|d   }t        |      }d}||kD  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      t        j                  |      dz  }t        j                  d|       dz   d|iz  }t        t        j                  |            dx}x}x}}|d   d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}y)u^   3a: reserved 상태가 timeout 이상 지속된 작업 감지 → warning + stale_tasks 반환   hoursr   z	task-20.1r   u   오래된 예약 작업rF   r   ,  timeout_secondsr   warningrd   rf   rg   u9   stale reserved 작업이 있으면 warning이어야 함: rj   rk   Nstale_tasksr   >z/%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} > %(py7)slenr   r   ri   py7u(   stale_tasks에 항목이 있어야 함: 
>assert %(py9)spy9r   assert %(py6)s)r   r'   r   r(   r   check_reservedrq   rr   rs   rt   ru   rv   r   r   r   r   )rw   r   old_timer-   timerrx   ry   rz   r{   r|   r}   r   @py_assert6r   @py_format8@py_format10s                   r   test_3a_stale_reserved_detectedz1TestCheckReserved.test_3a_stale_reserved_detected  su    LLNYQ%77BBD**#<(#+


   
;%%c%:h 	Q9 	Q9, 	Q?P?P	Q9 	Q 	QGPy   	Q 	QGPy $- 	Q 	Q?P?PGxP	Q 	Q 	Q=P=P	Q 	Q-( 	@s() 	@A 	@)A- 	@.?.?	@)A 	@ 	@9?	@ 	@'?'?  	@ 	@6?i  	@ 	@6?i ) 	@ 	@6?i * 	@ 	@6?i -. 	@ 	@.?.?6vh?	@ 	@ 	@,?,?	@ 	@ 	@m$Q'	2AkA2kAAAA2kAAA2AAAkAAAAAAAr   c                    t        j                         j                         }dddddd|dii}| j                  ||      }|j	                  d      }|d	   }d
}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	t        j                  d|       dz   d|	iz  }
t        t        j                  |
            dx}x}}|d   }g }||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	t        j                  d|       dz   d|	iz  }
t        t        j                  |
            dx}x}}y)u=   3b: 모든 작업이 timeout 미만일 때 ok, stale_tasks=[]r   z	task-21.1r   u   방금 예약된 작업rF   r   r   r   r   okrd   rf   rg   u.   신선한 reserved 작업이면 ok여야 함: rj   rk   Nr   u(   stale_tasks가 빈 배열이어야 함: )r   r'   r(   r   r   rq   rr   rs   rt   ru   rv   )rw   r   
fresh_timer-   r   rx   ry   rz   r{   r|   r}   s              r   test_3b_fresh_reserved_okz+TestCheckReserved.test_3b_fresh_reserved_ok  s    \\^--/
**#<(#-


   
; %%c%:h 	F4 	F4' 	F4E4E	F4 	F 	F<EI   	F 	F<EI $( 	F 	F4E4E<VHE	F 	F 	F2E2E	F 	Fm$ 	@ 	@$* 	@.?.?	@$ 	@ 	@6?i % 	@ 	@6?i )+ 	@ 	@.?.?6vh?	@ 	@ 	@,?,?	@ 	@ 	@r   c                 
   dddddddii}| j                  ||      }|j                  d      }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   }g }	||	k(  }|st        j                  d|fd||	f      t        j                  |      t        j                  |	      dz  }
t        j                  d|       dz   d|
iz  }t        t        j                  |            dx}x}}	y)uB   3c: reserved_at 필드가 없는 작업 → 무시 (에러 없음)r   z	task-22.1z	dev3-teamu    reserved_at 없는 예약 작업rF   )r   r   r   r   r   r   r   rm   z%(py1)s in %(py3)srx   rh   py3u(   결과에 status 필드가 있어야 함
>assert %(py5)spy5Nr   rd   rf   rg   u>   reserved_at 없으면 stale_tasks가 빈 배열이어야 함: rj   rk   )r   r   rq   rr   rs   r   r   r   rt   ru   rv   )rw   r   r-   r   rx   ry   r{   @py_format4r   rz   r|   r}   s               r   ,test_3c_reserved_without_reserved_at_ignoredz>TestCheckReserved.test_3c_reserved_without_reserved_at_ignored-  s\    **#E(	


   
; %%a%8Mx6!MMMx6MMMxMMMMMM6MMM6MMMM#MMMMMMMm$ 	V 	V$* 	VDUDU	V$ 	V 	VLUI % 	V 	VLUI )+ 	V 	VDUDULVHU	V 	V 	VBUBU	V 	V 	Vr   c                    t        j                         t        d      z
  j                         }dddddd|dii}|d	z  d
z  }|j	                  t        j                  |dd             t        j                  j                         }t        |      |d<   t        j                  g ddd|      }|j                  }d}||k(  }	|	st        j                  d|	fd||f      dt!        j"                         v st        j$                  |      rt        j&                  |      ndt        j&                  |      t        j&                  |      dz  }
t        j(                  d|j*                         dz   d|
iz  }t-        t        j.                  |            dx}x}	}t        j0                  |j2                        }|d   }d}	||	k(  }|st        j                  d|fd||	f      t        j&                  |      t        j&                  |	      dz  }t        j(                  d|       d z   d!|iz  }t-        t        j.                  |            dx}x}}	|d"   }t5        |      }	d}|	|kD  }|st        j                  d#|fd$|	|f      d%t!        j"                         v st        j$                  t4              rt        j&                  t4              nd%t        j&                  |      t        j&                  |	      t        j&                  |      d&z  }t        j(                  d'|       d(z   d)|iz  }t-        t        j.                  |            dx}x}	x}}y)*uJ   3d: CLI 실행 테스트: python3 task-timer.py check-reserved --timeout 0
   )secondsr   z	task-23.1r   u   CLI 테스트용 예약 작업rF   r   r
   r!   Fr"   r#   WORKSPACE_ROOT)python3r   zcheck-reservedz	--timeout0T)capture_outputtextenvr   rd   )z2%(py2)s
{%(py2)s = %(py0)s.returncode
} == %(py5)sproc)r   r   r   u6   CLI 실행이 성공해야 함 (returncode=0): stderr=z
>assert %(py7)sr   Nr   r   rf   rg   u5   timeout=0이면 reserved 작업이 감지돼야 함: rj   rk   r   r   r   r   r   u(   stale_tasks에 작업이 있어야 함: r   r   )r   r'   r   r(   r)   r*   r+   osenvironrP   r   
subprocessrunr8   rq   rr   r   r   r   rs   rt   r:   ru   rv   rR   r9   r   )rw   r   r   r-   r.   r   r   r   @py_assert4rz   r   r   outputry   r{   r|   r}   r   r   r   s                       r   'test_3d_cli_check_reserved_timeout_zeroz9TestCheckReserved.test_3d_cli_check_reserved_timeout_zeroC  sP    LLNYr%::EEG**#C(#+


 #X-0BB
djj%PQRSjjoo #M 2~~1d
  	S! 	S!# 	SARAR	S! 	S 	SLRF	S 	S:R:R  	S 	SIR  	S 	SIR  	S 	SIR #$ 	S 	SARARDT[[MR	S 	S 	S?R?R	S 	S DKK(h 	M9 	M9, 	M;L;L	M9 	M 	MCL9   	M 	MCL9 $- 	M 	M;L;LCF8L	M 	M 	M9L9L	M 	M-( 	@s() 	@A 	@)A- 	@.?.?	@)A 	@ 	@9?	@ 	@'?'?  	@ 	@6?i  	@ 	@6?i ) 	@ 	@6?i * 	@ 	@6?i -. 	@ 	@.?.?6vh?	@ 	@ 	@,?,?	@ 	@ 	@r   N)	r   r   r   r   r   r   r   r   r   r   r   r   r   r     s#    8@"B2@2V,"@r   r   c                   \    e Zd ZdZej
                  d        Z	 d
dZd Zd Z	d Z
d Zd	 Zy)TestDispatchResultFieldsu2   dispatch.py: 결과 필드 포함 여부 테스트c                 l    di i}|dz  dz  }|j                  t        j                  |dd             |S )u   빈 timer 파일 생성r   r
   r!   Fr"   r#   )r)   r*   r+   )rw   r   r-   r.   s       r   clean_timer_filez)TestDispatchResultFields.clean_timer_fileo  s?     r]
"X-0BB
djj%PQRSr   Nc           	         ddl }|ddi}t        dt        j                  |      d      }|j                  }|j
                  j                         }		 ||_        ddddd|_        t        d	|
      5  t        dd
      5  t        j                  |j                               }
dt        j                         j                         d|
d   d<   |j                  t        j                  |
dd             |j                  d||      cddd       cddd       ||_        |	|_        S # 1 sw Y   nxY w	 ddd       n# 1 sw Y   nxY w||_        |	|_        y# ||_        |	|_        w xY w)u"   mock subprocess로 dispatch 실행r   Nr5   zcron-field-test-001r6   r7   rb   r<   rA   rB   rD   rE   rF   rG   r   Fr"   r#   r   rL   )rM   r   r*   r+   rN   rO   rP   r   rR   rS   r   r'   r(   r)   )rw   r   r.   rT   rL   cron_responserV   r   rW   rX   rY   s              r   _dispatch_with_mockz,TestDispatchResultFields._dispatch_with_mockw  sz    	+ !#89M::m,
 -66+4499;	9(5O%!:"J(O$ 0{K 
6[Q 	J$8$8$:;B",'/||~'?'?'A0BwK, ))$**ReTU*VW*33#Yv 4 	 	
 
 );O%'8O$	 	 	
 
 
 );O%'8O$ );O%'8O$sC    E 3EB	D+
	E	E +D4	0E8	E E
E E,c                    | j                  ||dddi      }|d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }t        j                  d
|       dz   d|iz  }t        t        j                  |            dx}x}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }	t        j                  d|       dz   d|	iz  }
t        t        j                  |
            dx}}|d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }t        j                  d|       dz   d|iz  }t        t        j                  |            dx}x}}y)uB   dispatch 성공 결과에 cron_id 필드가 포함되는지 확인u   cron_id 테스트 작업r5   z
cron-12345r  r   r   rd   rf   rg      dispatched여야 함: rj   rk   Ncron_idrm   r   rx   r   u!   cron_id 필드가 있어야 함: r   r   u#   cron_id가 'cron-12345'여야 함: 
r  rq   rr   rs   rt   ru   rv   r   r   r   rw   r   r   rx   ry   rz   r{   r|   r}   r   r   s              r   test_4a_cron_id_in_resultz2TestDispatchResultFields.test_4a_cron_id_in_result  s   ))+&. * 
 hR<R</RRR<RRRRRR<RRR3I&1RRRRRRRRPyF"PPPyFPPPyPPPPPPFPPPFPPPP&Gx$PPPPPPPi  	;L 	; L0 	;):):	; L 	; 	;1: ! 	; 	;1: %1 	; 	;):):1&:	; 	; 	;':':	; 	; 	;r   c                 6   | j                  ||d      }|d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|       dz   d	|iz  }t        t        j                  |            d
x}x}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }	t        j                  d|       dz   d|	iz  }
t        t        j                  |
            d
x}}|d   }t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j                  d|       dz   d|iz  }t        t        j                  |            d
x}x}}y
)uM   dispatch 성공 결과에 expected_execution 필드가 포함되는지 확인u#   expected_execution 테스트 작업r   r   rd   rf   rg   r  rj   rk   Nexpected_executionrm   r   rx   r   u,   expected_execution 필드가 있어야 함: r   r      z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sr   	exec_timer   rh   r   rk   u2   expected_execution 포맷이 올바르지 않음: z
>assert %(py8)spy8r  rq   rr   rs   rt   ru   rv   r   r   r   r   )rw   r   r   rx   ry   rz   r{   r|   r}   r   r   r  r   r   @py_format9s                  r   $test_4b_expected_execution_in_resultz=TestDispatchResultFields.test_4b_expected_execution_in_result  sq   ))+1

 hR<R</RRR<RRRRRR<RRR3I&1RRRRRRRR# 	D#v- 	D2C2C	D#v 	D 	D:C) $ 	D 	D=CV	D 	D+C+C (. 	D 	D:C) (. 	D 	D2C2C:6(C	D 	D 	D0C0C	D 	D /0	9~ 	M 	M~# 	M 	M;L;L	M~ 	M 	MFLf	M 	M4L4L  	M 	MCL9  	M 	MFLf	M 	M4L4L  	M 	MCL9  	M 	MCL9  	M 	MCL9 "$ 	M 	M;L;L@L	M 	M 	M9L9L	M 	M 	Mr   c                    | j                  ||dd      }|d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d	|       d
z   d|iz  }t        t        j                  |            dx}x}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }	t        j                  d|       dz   d|	iz  }
t        t        j                  |
            dx}}|d   }t        |      }d}||kD  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      t        j                  |      dz  }t        j                  d|       dz   d|iz  }t        t        j                  |            dx}x}x}}y)u?   verify=True일 때 verify_note 필드가 포함되는지 확인u   verify 테스트 작업Tr   r   r   rd   rf   rg   r  rj   rk   Nverify_noterm   r   rx   r   u8   verify=True일 때 verify_note 필드가 있어야 함: r   r   r   r   r   r   r   u(   verify_note가 비어있으면 안 됨: r   r   r  )rw   r   r   rx   ry   rz   r{   r|   r}   r   r   r   r   r   r   r   s                   r   (test_4c_verify_true_includes_verify_notezATestDispatchResultFields.test_4c_verify_true_includes_verify_note  sM   ))+% * 
 hR<R</RRR<RRRRRR<RRR3I&1RRRRRRRR 	P}& 	P>O>O	P} 	P 	PFOi  	P 	PIO	P 	P7O7O !' 	P 	PFOi !' 	P 	P>O>OFvhO	P 	P 	P<O<O	P 	P-( 	@s() 	@A 	@)A- 	@.?.?	@)A 	@ 	@9?	@ 	@'?'?  	@ 	@6?i  	@ 	@6?i ) 	@ 	@6?i * 	@ 	@6?i -. 	@ 	@.?.?6vh?	@ 	@ 	@,?,?	@ 	@ 	@r   c                    | j                  ||dd      }|d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d	|       d
z   d|iz  }t        t        j                  |            dx}x}}d}||v}|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }	t        j                  d|       dz   d|	iz  }
t        t        j                  |
            dx}}y)uE   verify=False(기본값)일 때 verify_note 필드가 없는지 확인u   verify False 테스트 작업Fr   r   r   rd   rf   rg   r  rj   rk   Nr  )not in)z%(py1)s not in %(py3)srx   r   u2   verify=False일 때 verify_note가 없어야 함: r   r   r  r  s              r   #test_4d_verify_false_no_verify_notez<TestDispatchResultFields.test_4d_verify_false_no_verify_note  sD   ))++ * 
 hR<R</RRR<RRRRRR<RRR3I&1RRRRRRRR 	J}F* 	J8I8I	J}F 	J 	J@I	  	J 	JCI6	J 	J1I1I %+ 	J 	J@I	 %+ 	J 	J8I8I@I	J 	J 	J6I6I	J 	Jr   c                    | j                  ||dddi      }|d   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d	|       d
z   d|iz  }t        t        j                  |            dx}x}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }	t        j                  d|       dz   d|	iz  }
t        t        j                  |
            dx}}|d   }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|       d
z   d|iz  }t        t        j                  |            dx}x}}y)uG   cokacdir 응답에 id 필드가 없을 때 cron_id가 None인지 확인u   id 없는 응답 테스트r   r   r  r   rd   rf   rg   r  rj   rk   Nr  rm   r   rx   r   u!   cron_id 필드는 있어야 함: r   r   )is)z%(py1)s is %(py4)su5   id 없는 응답이면 cron_id가 None이어야 함: r  r  s              r   +test_4e_cron_id_none_when_no_id_in_responsezDTestDispatchResultFields.test_4e_cron_id_none_when_no_id_in_response  s   ))+(#T* * 
 hR<R</RRR<RRRRRR<RRR3I&1RRRRRRRRPyF"PPPyFPPPyPPPPPPFPPPFPPPP&Gx$PPPPPPPi  	MD 	M D( 	M;L;L	M D 	M 	MCL9 ! 	M 	MCL9 %) 	M 	M;L;LCF8L	M 	M 	M9L9L	M 	M 	Mr   )FN)r   r   r   r   r   r   r   r  r	  r  r  r  r  r   r   r   r   r   l  sC    <^^  :>#9J;M@
JMr   r   c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestTaskTimerImportu9   task-timer.py TaskTimer 클래스 직접 import 테스트c                     ddl }|j                  j                  dd      }|j                  j                  |      }|j                  j                  |       |S )u   task-timer.py 모듈 로드r   Nr   r   )r   r   r   r   r   r   )rw   r   r   r   s       r   _load_task_timer_modulez+TestTaskTimerImport._load_task_timer_module  sL    ~~556
 nn--d3$
r   c                    | j                         }t        j                         t        d      z
  j	                         }ddd|dii}|dz  dz  }|j                  t        j                  |d	d
             |j                  t        |            }|j                  d      }|d   }d}	||	k(  }
|
slt        j                  d|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	|d   }t!        |      }
d}|
|k(  }|st        j                  d|fd|
|f      dt#        j$                         v st        j&                  t               rt        j                  t               nddt#        j$                         v st        j&                  |      rt        j                  |      ndt        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}}|d   d    }d}	||	k(  }
|
slt        j                  d|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	|d   d!   }d}	||	k\  }
|
slt        j                  d"|
fd#||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	y)$u:   TaskTimer.check_reserved: 오래된 reserved 작업 감지r   )minutesr   z	task-30.1rF   rG   r
   r!   Fr"   r#   r   r   r   r   r   rd   rf   rg   r   rk   Nr   r   r  r   staler  zassert %(py8)sr  r   r   age_seconds)>=)z%(py1)s >= %(py4)sr   r   r'   r   r(   r)   r*   r+   r   r   r   rq   rr   rs   ru   rv   r   r   r   r   )rw   r   r   r   r-   r.   r   rx   ry   rz   r{   r|   r}   r#  r   r   r  s                    r   $test_task_timer_check_reserved_stalez8TestTaskTimerImport.test_task_timer_check_reserved_stale  s4   **,LLNYr%::EEG(#+

 #X-0BB
djj%PQRSS-?@%%b%9h,9,9,,,,9,,,,,,9,,,,,,,}%5zQzQzQss55zQQx	"1k1"k1111"k111"111k1111111Qx&,",&",,,,&",,,&,,,",,,,,,,r   c                 2   | j                         }t        j                         j                         }ddd|dii}|dz  dz  }|j	                  t        j                  |dd	             |j                  t        |      
      }|j                  d      }|d   }d}	||	k(  }
|
slt        j                  d|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	|d   }g }	||	k(  }
|
slt        j                  d|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	y)u<   TaskTimer.check_reserved: 신선한 reserved 작업이면 okr   z	task-31.1rF   rG   r
   r!   Fr"   r#   r   r   r   r   r   rd   rf   rg   r   rk   Nr   )r   r   r'   r(   r)   r*   r+   r   r   r   rq   rr   rs   ru   rv   )rw   r   r   r   r-   r.   r   rx   ry   rz   r{   r|   r}   s                r   !test_task_timer_check_reserved_okz5TestTaskTimerImport.test_task_timer_check_reserved_ok  s;   **,\\^--/
(#-

 #X-0BB
djj%PQRSS-?@%%c%:h'4'4''''4''''''4'''''''m$**$****$***$**********r   c                    | j                         }ddddiii}|dz  dz  }|j                  t        j                  |dd	             |j	                  t        |      
      }|j                  d      }|d   }d}||k(  }	|	slt        j                  d|	fd||f      t        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}	}|d   }g }||k(  }	|	slt        j                  d|	fd||f      t        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}	}y)u:   TaskTimer.check_reserved: reserved_at 없는 작업 무시r   z	task-32.1r   rF   r
   r!   Fr"   r#   r   r   r   r   rd   rf   rg   r   rk   Nr   )r   r)   r*   r+   r   r   r   rq   rr   rs   ru   rv   )rw   r   r   r-   r.   r   rx   ry   rz   r{   r|   r}   s               r   -test_task_timer_check_reserved_no_reserved_atzATestTaskTimerImport.test_task_timer_check_reserved_no_reserved_at!  s(   **, j

 #X-0BB
djj%PQRSS-?@%%a%8h'4'4''''4''''''4'''''''m$**$****$***$**********r   c                    | j                         }t        j                         t        d      z
  j	                         }t        j                         j	                         }dd|dd|dd|ddi}|d	z  d
z  }|j                  t        j                  |dd             |j                  t        |            }|j                  d      }|d   }	d}
|	|
k(  }|slt        j                  d|fd|	|
f      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
|d   }t!        |      }
d}|
|k(  }|st        j                  d|fd|
|f      dt#        j$                         v st        j&                  t               rt        j                  t               ndt        j                  |      t        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}
x}}|d   d   d    }	d!}
|	|
k(  }|slt        j                  d|fd|	|
f      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}	x}}
y)"uI   TaskTimer.check_reserved: stale와 fresh가 섞인 경우 stale만 반환r"   r   r   rF   rG   r   )r   r    )	task-33.1z	task-33.2z	task-33.3r
   r!   Fr#   r   r   r   r   r   rd   rf   rg   r   rk   Nr   r   )z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)sr   r   zassert %(py9)sr   r   r   r-  r&  )rw   r   r   r   r   r-   r.   r   rx   ry   rz   r{   r|   r}   r   r   r   r   r   s                      r   $test_task_timer_check_reserved_mixedz8TestTaskTimerImport.test_task_timer_check_reserved_mixed7  s   **,LLNYQ%77BBD\\^--/
(#+
 )#-
 ("*

  #X-0BB
djj%PQRSS-?@%%c%:h,9,9,,,,9,,,,,,9,,,,,,,-(.s().Q.)Q....)Q......s...s...(...)...Q.......m$Q'	2AkA2kAAAA2kAAA2AAAkAAAAAAAr   N)	r   r   r   r   r   r'  r)  r+  r.  r   r   r   r  r    s    C	-2+,+,Br   r  )N)"r   builtinsr   _pytest.assertion.rewrite	assertionrewriterq   r*   r   r   systempfiler   r   r   pathlibr   unittest.mockr   r   r   r   pathinsertr   r   r/   r3   r]   r_   r   r   r   r  r   r   r   <module>r9     s      	  
   (  5 5  ( ) 
 
  $  &2rRI RIr|J |JF~@ ~@JqM qMpqB qBr   